sample1 = Neurons1 sample2 = Neurons2 sample3 = Glia1 - Astrocytes (CD44+) sample4 = Glia2 - Radial Glia (CD44-)
In HPC I have run steps of scrnabox (custom pipeline in progress) 1. Cell Ranger for feature seq 2. Create Seurat Objects 3. Apply minimum filtering and calculate percent mitochondria.
I have technical 3 replicates with hashtag labels at this point I haven’t yet demultiplex the hashtags. The data here will be treated as one sample. I sorted three separate samples and pooled them together.
0 - NPC or early neurons 1 - immature excitatory neurons 2 - NPC or early neurons 3 - RG or Oligos 4- Dopaminergic neurons - possibly early 5 - NPC or early neurons 6 - Radial GLia
After predicting with the brain data I think using a higher cluster number will work better
Library of tissue cell types for up regulated genes per cluster 0 - hypothalmus, DA A13 1- neural plate, Radial Glia 2 - Neural stem 3 - stromal, astro OPC 4 - Neurons 5 - endothelial, pericyte 6 - maybe neurons maybe not
After predicting with the brain data Bhaduri Midbrain and Striatum - choose main cell types to label Tried the predictions with more clusters 0-9 res 1.2
Next Repeat everything for Neurons2
# explore filtering
seu <- Neurons2
seu
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 2000)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 350)
VlnPlot(seu, pt.size = 0.10, features = c("nCount_RNA"), y.max = 2000)
# filter more cells
seu.ft <- subset(seu, subset = nFeature_RNA > 300 & nCount_RNA > 500 & nCount_RNA < 10000)
seu.ft
# 17604 samples with 250 nFeature_RNA
# 9657 with nFeature 300 and nCOunt 500
Doublet finder
suppressMessages(require(DoubletFinder))
# filtering out MALAT1 and mitochondrial genes
seu.ft <- seu.ft[!grepl("MALAT1", rownames(seu)), ]
seu.ft <- seu.ft[!grepl("^MT-", rownames(seu.ft)), ]
# like in the tutorial I'm following MALAT1 is the top most expressed gene. The top genes are a lot of MT and Ribosomal genes
seu.ft[["percent.rb"]] <- PercentageFeatureSet(seu.ft, pattern = "^RP")
seu.d = NormalizeData(seu.ft)
seu.d = FindVariableFeatures(seu.d, verbose = F)
seu.d = ScaleData(seu.d, vars.to.regress = c("nFeature_RNA", "percent.mt"),
verbose = F)
seu.d = RunPCA(seu.d, verbose = F, npcs = 30)
seu.d = RunUMAP(seu.d, dims = 1:10, verbose = F)
nExp <- round(ncol(seu.d) * 0.08) # expect more doublets because there is a lot more cells
seu.d <- doubletFinder_v3(seu.d, pN = 0.25, pK = 0.09, nExp = nExp, PCs = 1:10)
# name of the DF prediction can change, so extract the correct column name.
DF.name = colnames(seu.d@meta.data)[grepl("DF.classification", colnames(seu.d@meta.data))]
cowplot::plot_grid(ncol = 2, DimPlot(seu.d, group.by = "orig.ident") + NoAxes(),
DimPlot(seu.d, group.by = DF.name) + NoAxes())
VlnPlot(seu.d, features = "nFeature_RNA", group.by = DF.name, pt.size = 0.1)
Remove the doublet cells
seu.d <- seu.d[, seu.d@meta.data[, DF.name]== "Singlet"]
dim(seu.d)
dim(seu)
# 9657 cells pre filter
# 8884 cells after filtering
# note the percent doubles expected is close to the percent detected
Repeat workflow with doublet removed data and find clusters for
seu <- NormalizeData(seu.d, normalization.method = "LogNormalize", scale.factor = 10000)
seu <- FindVariableFeatures(seu, selection.method = "vst", nfeatures = 2000)
seu <- ScaleData(seu)
seu <- RunPCA(seu)
seu <- RunUMAP(seu, reduction = "pca", n.neighbors = 43, dims = 1:30)
DimPlot(seu, reduction = "umap")
seu.q <- FindNeighbors(seu, dims = 1:25, k.param = 43)
seu.q <- FindClusters(seu.q, resolution = c(0,0.2,0.4,0.6))
library(clustree)
clustree(seu.q)
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.2')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.4')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.6')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.1.2')
Label cell types using the label transfer
# SNCA and control midbrain organoids 165 days in culture
MBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AST23_BrainComm/MBOclusters_names29072021.rds")
# Midbrain AIW002 120 days in culture
AIWMBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio120days/MOintegratedClusterK123res0.8.names_nov16_2021")
# Midbrain AIW002 60 days in culture
AIW60 <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio60days/AWI002ParkinKOPinkKO60days_labels_14052022.rds")
# query
#seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/NeuronsFilteredSeu28092022.RDS")
#first predict with the MBO data
Idents(MBO) <- "cluster_labels"
DefaultAssay(MBO) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = MBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = MBO$cluster_labels)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAST23.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAST23.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id))
pr.t.lables <- as.data.frame(prop.table(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id)))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# clusters don't break up by the predicted cell types
############ another predictions now using the AIW organoids
Idents(AIWMBO) <- "res08names"
DefaultAssay(AIWMBO) <- "RNA"
anchors <- FindTransferAnchors(reference = AIWMBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIWMBO$res08names)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAIW.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAIW.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id))
pr.t.lables <- as.data.frame(prop.table(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id)))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# the predicted cell types make more sense from the AIW002 organoid
# now predict with the AIW002 60 days organoid
Idents(AIW60) <- "cluster.ids"
DefaultAssay(AIW60) <- "RNA"
anchors <- FindTransferAnchors(reference = AIW60, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIW60$cluster.ids)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW60.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id))
pr.t.lables <- as.data.frame(prop.table(table(seu.q$RNA_snn_res.0.2, seu.q$predicted.id)))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# save ojbect with predicitons
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2PredictionsSeu30092022.RDS")
Predict from Brain Bhahani
seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2LabelsSeu30092022.RDS")
# read in the reference dataset
# from Bhaduri midbrain and striatum
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
table(seu.r$cell_type)
astrocyte dividing endothelial interneuron microglia neuron
310 351 321 4 30 1596
oligodendrocyte radial glia
195 162
table(seu.r$cell_class)
dividing endothelial glia microglia neuron
351 321 667 30 1600
table(seu.r$cell_cluster)
Astrocyte_4 Astrocyte_5 Dividing_11 Dividing_2 Dividing_3
211 99 10 71 103
Dividing_4 Dividing_42 Dividing_5 Endo_11 Endo_13
38 1 128 28 1
Endo_15 Endo_4 Endo_5 Endo_7 Endo_8
10 144 14 93 31
GW14_Microglia_27 Interneuron_1 Interneuron_24 Interneuron_9 Microglia_10
4 1 2 1 9
Microglia_2 Microglia_8 Neuron_100 Neuron_2 Neuron_22
9 8 1 57 44
Neuron_37 Neuron_4 Neuron_5 Neuron_98 Oligo_11
104 249 1049 92 35
Oligo_2 Oligo_6 Oligo_8 RG_1 RG_37
58 83 19 80 1
RG_4 RG_45 RG_6 RG_7
22 33 25 1
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 1051 anchors
Filtering anchors
Retained 318 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_4 Dividing_3 Dividing_5 Endo_4 Endo_7 Endo_8 Neuron_2 Neuron_22
2105 24 20 987 346 4 32 1
Neuron_37 Neuron_4 Neuron_98 Oligo_11 Oligo_2 RG_1 RG_45 RG_6
3353 1070 866 2 5 6 1 62
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
Neuron_4 Neuron_37 Astrocyte_4 Neuron_98 Endo_4 Endo_7 RG_6 RG_1
1070 3353 2105 866 987 346 62 6
Dividing_3 Endo_8 Oligo_11 Neuron_2 Dividing_5 Oligo_2 Neuron_22 RG_45
24 4 2 32 20 5 1 1
DimPlot(seu.q, group.by = 'Bha.mid.stri.pred')

DimPlot(seu.q, group.by = 'subgroups')

# do the predictions differ with the main cell type groups instead of the cluster in the reference data?
Idents(seu.r) <- "cell_type"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 1051 anchors
Filtering anchors
Retained 318 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_type)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
astrocyte dividing endothelial neuron oligodendrocyte radial glia
1294 34 1386 5606 53 511
DimPlot(seu.q, group.by = 'predicted.id')

# good largest prediction is neurons.
See the top predictions for each cluster in Neurons2 res 06
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
What cell types are predicted across the 3 references
0 - Neurons early , NPC, neurons excitatory 1 - Neurons early, NPC 2 - Neurons early, NPC, neurons excitatory some DA neurons 3 - Oligo, RG, 4 - Excitatory neurons, NPC, early neurons 5 - DA neurons, early DA neurons 6 - neurons immature NPC 7 - DA neurons 8 - RG, oligo, OPC, NPC 9 - Radial Glia 10 - NPC, neurons, oligo 11 - NPC, neurons, oligo
Find cluster markers and see how those would annotate
Idents(seu.q) <- 'RNA_snn_res.0.6'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
write.csv(ClusterMarkers,"/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/Neurons1ClusterMarkers11.csv")
Cluster 0 has fewer markers. 2 and 5 have similar up reg markers 3 and 4 also overlap
Look at the cluster markers in cell type libraries for Neurons 2
library(enrichR)
db <- c('Allen_Brain_Atlas_up','Descartes_Cell_Types_and_Tissue_2021',
'CellMarker_Augmented_2021','Azimuth_Cell_Types_2021')
# enrichr(genes, databases = NULL)
# cluster 0
N1.c0 <- ClusterMarkers %>% filter(cluster == 0 & avg_log2FC > 0)
genes <- N1.c0$gene
N1.c0.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c0.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c0.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c0.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c0.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
# cluster 1
N1.c1 <- ClusterMarkers %>% filter(cluster == 1 & avg_log2FC > 0)
genes <- N1.c1$gene
N1.c1.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c1.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c1.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c1.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c1.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c1.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c1.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c1.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c1.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 1; olfactory bulb, neural plate, maybe Radial Glia,
N1.c2 <- ClusterMarkers %>% filter(cluster == 2 & avg_log2FC > 0)
genes <- N1.c2$gene
N1.c2.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c2.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c2.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c2.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c2.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c2.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c2.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c2.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c2.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 3
N1.c3 <- ClusterMarkers %>% filter(cluster == 3 & avg_log2FC > 0)
genes <- N1.c3$gene
N1.c3.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c3.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c3.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c3.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c3.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c3.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c3.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c3.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c3.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 4
N1.c4 <- ClusterMarkers %>% filter(cluster == 4 & avg_log2FC > 0)
genes <- N1.c4$gene
N1.c4.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c4.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c4.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c4.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c4.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c4.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c4.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c4.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c4.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 5
N1.c5 <- ClusterMarkers %>% filter(cluster == 5 & avg_log2FC > 0)
genes <- N1.c5$gene
N1.c5.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c5.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c5.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c5.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c5.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c5.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c5.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c5.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c5.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 6
N1.c6 <- ClusterMarkers %>% filter(cluster == 6 & avg_log2FC > 0)
genes <- N1.c6$gene
N1.c6.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c6.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c6.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c6.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c6.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c6.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# other clusters - change the cluster number
N1.c6 <- ClusterMarkers %>% filter(cluster == 11 & avg_log2FC > 0)
genes <- N1.c6$gene
N1.c6.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c6.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c6.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c6.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c6.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c6.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c6.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
cluster 0 - astrocyte, radial glia, microglia, striatum - very few genes in these terms cluster 1 - adrenal, thalmus, endothelial, astrocytes, neurons clusters 2 - neural plate, stratum, neurons, NPC, stem, astro,neuroendocrine cluster 3 - brain molecular layer, endothelial, astrocyte embryonic, cluster 4 - DG, striatum CA3, GABAergic neurons cluster 5 - DG, neurons, Glutamatergic neurons cluster 6 - neurons, astrocyte, microglia, GABAneurons cluster 7 - neurons, glut and gaba cluster 8 - astrocyte, endothelial, pericyte cluster 9 - epithelial, embryonic astrocytes, GABA neurons cluster 10 - epithelial cluster 11 - endothelial, immune cells T cells
Expression of markers genes in Neurons2
feature_list = c("MKI67","SOX2","POU5F1","DLX2","PAX6","SOX9","HES1","NES","RBFOX3","MAP2","NCAM1","CD24","GRIA2","GRIN2B","GABBR1","GAD1","GAD2","GABRA1","GABRB2","TH","ALDH1A1","LMX1B","NR4A2","CORIN","CALB1","KCNJ6","CXCR4","ITGA6","SLC1A3","CD44","AQP4","S100B", "PDGFRA","OLIG2","MBP","CLDN11","VIM","VCAM1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = feature_list) +RotatedAxis()
PD_poulin = c("TH","SLC6A3","SLC18A2","SOX6","NDNF","SNCG","ALDH1A1","CALB1","TACR2","SLC17A6","SLC32A1","OTX2","GRP","LPL","CCK","VIP")
DoHeatmap(seu.q, features = PD_poulin, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = PD_poulin)+RotatedAxis()
ealryNeur = c("DCX","NEUROD1","TBR1")
proliferation = c("PCNA","MKI67")
neuralstem = c("SOX2","NES","PAX6","MASH1")
feature_list <- c("DCX","NEUROD1","TBR1","PCNA","MKI67","SOX2","NES","PAX6","MASH1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = feature_list)+RotatedAxis()
# no proliferation marker expression PCNA or MKI67
# cluster 4 DA neurons - shows early neuron marker and low PAX 4
# cluster 3 has higher SOX2 - neuroblast marker / NPC marker
mat_neuron = c("RBFOX3","SYP","DLG45","VAMP1","VAMP2","TUBB3","SYT1","BSN","HOMER1","SLC17A6")
# NeuN is FOX3 - RBFOX3
# PSD95 also SP-90 or DLG4
# VGLUT2 is SLC17A6
DoHeatmap(seu.q, features = mat_neuron, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
# cluster 4 also show mature neuron markers
DotPlot(seu.q, features = mat_neuron)+RotatedAxis()
# excitatory neuron markers
ex = c("GRIA2","GRIA1","GRIA4","GRIN1","GRIN2B","GRIN2A","GRIN3A","GRIN3","GRIP1","CAMK2A")
DoHeatmap(seu.q, features = ex, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = ex)+RotatedAxis()
# inhibitory neuron markers
inh = c("GAD1","GAD2", "GAT1","PVALB","GABR2","GABR1","GBRR1","GABRB2","GABRB1","GABRB3","GABRA6","GABRA1","GABRA4","TRAK2")
DoHeatmap(seu.q, features = inh, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = inh)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
### glia markers
microglia = c("PTPRC","AIF1","ADGRE1") # ADGRE1 is a microglia marker F4/80, CD45 is PTPRC, gene name IBA1 is AIF1
astolgNPCpromicro = c("GFAP","S100B","SLC1A2","MBP","SOX10","SPP1","DCX","NEUROD1","TBR1","PCNA","MKI67","PTPRC","AIF1","ADGRE1")
# note GLT1 is EAAT2 which is SLC1A2 glutatmate transporter
# epithelial
epi = c("HES1","HES5","SOX2","SOX10","NES","CDH1","NOTCH1") # e-cadherin is CDH1
DoHeatmap(seu.q, features = astolgNPCpromicro, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = astolgNPCpromicro, group.by = 'RNA_snn_res.0.6')+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
DoHeatmap(seu.q, features = epi, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = epi, group.by = 'RNA_snn_res.0.6')+RotatedAxis()
# also add Radial glia marker overlap with Glia and Neurons
features <- c("PTPRC","AIF1","ADGRE1", "VIM", "TNC","PTPRZ1","FAM107A","HOPX","LIFR",
"ITGB5","IL6ST")
DoHeatmap(seu.q, features = features, size=3, angle =90, group.bar.height = 0.02, group.by = 'RNA_snn_res.0.6')
DotPlot(seu.q, features = features, group.by = 'RNA_snn_res.0.6')+RotatedAxis()
Label the Neuron2 FACS population
Idents(seu.q) <- 'RNA_snn_res.0.6'
cluster.ids <- c("Neurons1","immatureNeurons1","Neurons2",
"Other","DAneurons1","DAneurons2","immatureNeurons2",
"DAneurons3","RG","immatureNeurons1","Epithelial","Endothelial")
unique(seu.q$RNA_snn_res.0.6)
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$subgroups <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'subgroups', repel = TRUE)
# label again with just numbering
# then I'll find markers in pairs to distinguish groups.
Idents(seu.q) <- 'RNA_snn_res.0.6'
cluster.ids <- c("Neurons1","Neurons2","Neurons3",
"Other","DAneurons1","DAneurons2","Neurons4",
"DAneurons3","RG","Neurons2","Epithelial","Endothelial")
unique(seu.q$RNA_snn_res.0.6)
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$number.groups <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'number.groups', repel = TRUE)
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2LabelsSeu30092022.RDS")
Find markers in pairs to go back and classify the subgroups. Will need to return to this for Neurons1 FACS
Neurons 5 and Neurons2 had similar markers and were merged Subset again
# faster to make a subset objects of only neurons and use find all markers
neuron.sub <- subset(seu.q, idents = c("Neurons1","Neurons2","Neurons3",
"Neurons4"))
neuron.sub.markers <- FindAllMarkers(neuron.sub)
top5 <- neuron.sub.markers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(neuron.sub, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
DotPlot(neuron.sub, features = top5$gene) + RotatedAxis()
# neurons 5 was joined to Neurons 2
# cluster markers done again
# Neurons1 : MGP (targeting neural projections BMP signaling), HPD (excitatory and inibitory, could be cell adhesion or apoptosis), MSX1 (transcription factor BMP signaling, midbrain marker, developmental), CYP1B1 (redox homeostatis)
# MGP, HPD, MSX1, CYP1B1
# there isn't a good proportion of cells expression any of the markers, Neurons2 has the best amount
# Neurons2: TFP12 serine protease melatonin conversion, PTN (cytokine signaling), LYgH (inhances nAChRs), S100A10 (modulates serotonin receptor), IFI27 (antiviral activity)
#TFP12,PTN, LY6H, S100A10, IFI27
# Neurons3: SOX4, ASCL1 (neurogenesis),
# SOX4 is a marker of 3 but has high expression in 4 as well
# Neurons4: PCAT4 (not noted in neurons), TPH1 (5HT synthesis), GK5 (neuronal maintainance), SST (somatostatin, GABA spike regulation), TTR (neural protective in AD)
# PCAT4, TPH1, GK5, SST, TTR
# markers to use
# Neurons1: MSX1, CYP1B1
# Neurons2: LY6H, S100A10
# Neurons3: SOX4, ASCL1 (Neurogenesis) Immature
# Neurons4: GK5, SST More mature
# Maybe neurons 1 and 2 could be merged
# lets see how the markers would look
neurons.1and2 <- FindMarkers(neuron.sub, ident.1 = c("Neurons1","Neurons2"),
ident.2 = c("Neurons3","Neurons4"))
top10 <- neurons.1and2 %>% top_n(n=10, wt = avg_log2FC)
ft.up <- rownames(top10) # up in Neurons1 and 3
top10 <- neurons.1and2 %>% top_n(n=-10, wt = avg_log2FC)
ft.down <- rownames(top10)
features <- c(ft.up,ft.down)
DoHeatmap(neuron.sub, features = features, size=3, angle =90, group.bar.height = 0.02)
DotPlot(neuron.sub, features = features) + RotatedAxis()
# all the markers were up regulated in neurons2 and not really neurons1
# I'll keep them separated
Use subgrouping and find cluster markers to look at neuronal subtypes.
# faster to make a subset objects of only neurons and use find all markers
neuron.sub <- subset(seu.q, idents = c("DAneurons1","DAneurons2","DAneurons3"))
neuron.sub.markers <- FindAllMarkers(neuron.sub)
top5 <- neuron.sub.markers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(neuron.sub, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
DotPlot(neuron.sub, features = top5$gene) + RotatedAxis()
# markers are much clearer for the DA neuron subgroups
# DA neurons 1: WIF1, CYP1B1, IGFBP3, HPD, WFIKKN2
# WIF1 (secreted WNT inhibitor, promotes regeneration), CYP1B1 (redox homeostatis), IGFBP3 (prolactin secretion regulation hypothalmus), HPD (neuro protective), WFIKKN2 (Receptor for TNC)
# DA neurons2: CDH7, RUNX1T1, ASCL1, DLK1, MEG3
# CDH7 (neuro circuitry development, SEMA), RUNX1T1 (neuronal differentiation), ASCL1 (neuronal differentiation), DLK1 (neural differentation), MEG3 (neural homeostatis)
# DA neurons3: PCAT4, NEUROD1, NCKAP5, GK5, SST
# PCAT4 (dendritic growth), NEUROD1 (neural differentation), NCKAP5 (Excitory neurons), GK5 (TH ), SST (regualates spike times)
# DA neurons1: CYP1B1, IGFBP3
# DA neurons2: RUNX1T1, ASCL1
# DA neurons3: NEUROD1, NCKAP5
Name the Neurons2 FACS population with the Neuron subtype latter.
Idents(seu.q) <- 'RNA_snn_res.0.6'
cluster.ids <- c("Neurons-MSX1","Neurons-LY6H","Neurons-ASCL1",
"Other","DAneurons-CYP1B1","DAneurons-ASCL1","Neurons-GK5",
"DAneurons-NEUROD1","RG","Neurons-LY6H","Epithelial","Endothelial")
unique(seu.q$RNA_snn_res.0.6)
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$cellsubgroups <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'cellsubgroups', repel = TRUE)
preduction summary Cell_Types
0 neurons Neurons 1 NPC/oligo/astro/neurons/RG Neurons 2 neurons Neurons 3 NPC/RG/Astro Neurons 4 RG/endo RG 5 DA neurons Neurons 6 Neurons Neurons 7 DA neurons Neurons 8 RG/endo Endothelial 9 RG/astro Astro 10 opc/npc/astro/neurons Neurons 11 neurons/dividing/RG Neurons
Clustering higher res res 1.2 0 Neurons 1 Neurons 2 Neurons 3 Astro/RG/Neurons 4 RG/Astro/Neurons 5 RG/Neurons/NPC 6 RG/NPC/Endo 7 Neurons DA 8 Neurons 9 Neurons DA 10 Neurons DA 11 RG/Endo 12 RG/Astro 13 RG/Astro/Neurons 14 Neurons/RG
Idents(seu.q) <- 'RNA_snn_res.1.2'
# highlight the DA neurons
cluster.ids <- c("Neurons","Neurons","NPC",
"Neurons","Neurons","Neurons",
"Neurons DA","Neurons","Neurons DA",
"Neurons DA", "Neurons DA","Endothelial",
"RG/Astro","RG/Astro/Neurons","Neurons/RG")
cluster.ids <- c("Neurons","Neurons","Neurons",
"Neurons","Neurons","NPC",
"Neurons","Neurons","Neurons",
"Neurons", "Neurons","Endothelial",
"Astrocytes","Radial Glia","Radial Glia")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Types <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Types', repel = TRUE)

#DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'RNA_snn_res.1.2', repel = TRUE)
#clustree(seu.q)
# save the Neurons2 with labels
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Neurons2LabelsSeu30092022.RDS")
FACS population Glia1 (should be astrocytes)
# explore filtering
seu <- Glia1
seu
#
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 1000)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 500)
VlnPlot(seu, pt.size = 0.10, features = c("nCount_RNA"), y.max = 2000)
# filter more cells
seu.ft <- subset(seu, subset = nFeature_RNA > 300 & nCount_RNA > 500 & nCount_RNA < 10000)
seu.ft
# still a lot of cells 47295
# will likely remove a lot more with the doublet finder
saveRDS(seu.ft, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1AstroSeu01102022.RDS")
seu.ft <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1AstroSeu01102022.RDS")
seu.ft <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1AstroSeu01102022.RDS")
Doublet finder
suppressMessages(require(DoubletFinder))
# filtering out MALAT1 and mitochondrial genes
seu.ft <- seu.ft[!grepl("MALAT1", rownames(seu.ft)), ]
seu.ft <- seu.ft[!grepl("^MT-", rownames(seu.ft)), ]
# like in the tutorial I'm following MALAT1 is the top most expressed gene. The top genes are a lot of MT and Ribosomal genes
seu.ft[["percent.rb"]] <- PercentageFeatureSet(seu.ft, pattern = "^RP")
# down sample there are too many cells to run doublet finder
seu.sub <- subset(seu.ft, downsample = 20000)
seu.d = NormalizeData(seu.sub)
seu.d = FindVariableFeatures(seu.d, verbose = F)
seu.d = ScaleData(seu.d, vars.to.regress = c("nFeature_RNA", "percent.mt"),
verbose = F)
seu.d = RunPCA(seu.d, verbose = F, npcs = 15)
seu.d = RunUMAP(seu.d, dims = 1:10, verbose = F)
nExp <- round(ncol(seu.d) * 0.15) # expect more doublets because there is a lot more cells
seu.d <- doubletFinder_v3(seu.d, pN = 0.25, pK = 0.09, nExp = nExp, PCs = 1:10)
# the memory limit is reached here - I could run on compute canada
# For now I'll downsample
# this works
# name of the DF prediction can change, so extract the correct column name.
DF.name = colnames(seu.d@meta.data)[grepl("DF.classification", colnames(seu.d@meta.data))]
cowplot::plot_grid(ncol = 2, DimPlot(seu.d, group.by = "orig.ident") + NoAxes(),
DimPlot(seu.d, group.by = DF.name) + NoAxes())
VlnPlot(seu.d, features = "nFeature_RNA", group.by = DF.name, pt.size = 0.1)
Remove the doublet cells
seu.d <- seu.d[, seu.d@meta.data[, DF.name]== "Singlet"]
dim(seu.d)
dim(seu.sub)
# 20000 pre filter
# creates the expected percentage
Repeat workflow with doublet removed data and find clusters for
seu <- NormalizeData(seu.d, normalization.method = "LogNormalize", scale.factor = 10000)
seu <- FindVariableFeatures(seu, selection.method = "vst", nfeatures = 2000)
seu <- ScaleData(seu)
seu <- RunPCA(seu)
seu <- RunUMAP(seu, reduction = "pca", n.neighbors = 43, dims = 1:30)
DimPlot(seu, reduction = "umap")
seu.q <- FindNeighbors(seu, dims = 1:25, k.param = 43)
seu.q <- FindClusters(seu.q, resolution = c(0,0.2,0.4,0.6))
seu.q <- FindClusters(seu.q, resolution = c(0,0.05,0.1,0.8))
library(clustree)
clustree(seu.q)
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.05')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.1')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.2')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.4')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.6')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.8')
Look at some expression markers in a feature plot
# genes reported up in Astrocytes
FeaturePlot(seu.q, features = c("GFAP","S100B","AQP4","SLC1A3","GJA1",
"APOE","TEAD1","GSTA4","SOX9",
"VIM","HMG20A","ALDH1L1"))
# almost no GFAP expression and lots of S100B everywhere
Predict cell types
# SNCA and control midbrain organoids 165 days in culture
MBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AST23_BrainComm/MBOclusters_names29072021.rds")
# Midbrain AIW002 120 days in culture
AIWMBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio120days/MOintegratedClusterK123res0.8.names_nov16_2021")
# Midbrain AIW002 60 days in culture
AIW60 <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio60days/AWI002ParkinKOPinkKO60days_labels_14052022.rds")
#first predict with the MBO data
Idents(MBO) <- "cluster_labels"
DefaultAssay(MBO) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = MBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = MBO$cluster_labels)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAST23.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAST23.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# clusters don't break up by the predicted cell types
############ another predictions now using the AIW organoids
Idents(AIWMBO) <- "res08names"
DefaultAssay(AIWMBO) <- "RNA"
anchors <- FindTransferAnchors(reference = AIWMBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIWMBO$res08names)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAIW.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAIW.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# the predicted cell types make more sense from the AIW002 organoid
# now predict with the AIW002 60 days organoid
Idents(AIW60) <- "cluster.ids"
DefaultAssay(AIW60) <- "RNA"
anchors <- FindTransferAnchors(reference = AIW60, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIW60$cluster.ids)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW60.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# save ojbect with predicitons
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1PredictionsSeu01102022.RDS")
Predict with the brain scRNAseq
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 752 anchors
Filtering anchors
Retained 108 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_4 Endo_4 Neuron_98 RG_1
7791 3019 2 9188
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
Astrocyte_4 Endo_4 RG_1 Neuron_98
7791 3019 9188 2
DimPlot(seu.q, group.by = 'Bha.mid.stri.pred')

DimPlot(seu.q, group.by = 'subgroups')

# do the predictions differ with the main cell type groups instead of the cluster in the reference data?
Idents(seu.r) <- "cell_type"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 752 anchors
Filtering anchors
Retained 108 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_type)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
astrocyte endothelial neuron radial glia
3747 7012 2152 7089
DimPlot(seu.q, group.by = 'predicted.id')

# AIW002 120 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW120 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw120 <- top.pred.celltype.AIW120[order(top.pred.celltype.AIW120$Var1,-top.pred.celltype.AIW120$Freq),]
row.names(df.top.aiw120) <- NULL
df.top.aiw120$I <- row.names(df.top.aiw120)
# AIW002 60 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW60 <-as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw60 <- top.pred.celltype.AIW60[order(top.pred.celltype.AIW60$Var1,-top.pred.celltype.AIW60$Freq),]
row.names(df.top.aiw60) <- NULL
df.top.aiw60$I <- row.names(df.top.aiw60)
# AST23 165 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AST23 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.AST23 <- top.pred.celltype.AST23[order(top.pred.celltype.AST23$Var1,-top.pred.celltype.AST23$Freq),]
row.names(df.top.AST23) <- NULL
df.top.AST23$I <- row.names(df.top.AST23)
### add in the prediction from brain data Bhaduri midbrain and striatum
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha.mid.stri.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha <- top.pred.celltype.Bha[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha) <- NULL
df.top.Bha$I <- row.names(df.top.Bha)
## these are calculated below
### add in the prediction from brain whole brain data Bhaduri midbrain down sampled
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha1 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha1 <- top.pred.celltype.Bha1[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha1) <- NULL
df.top.Bha1$I <- row.names(df.top.Bha1)
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table <- merge(pred.table, df.top.Bha1, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha1, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
NA
These predictions are not good. There are several astrocyte markers by expression levels. Everything is predicted as Radial glia or oligo dendrocytes
Try to predict with the whole brain and see if it’s different
Idents(seu.q) <- 'predicted.id'
seu.q$Bha <- Idents(seu.q)
print(table(seu.q$Bha))
Oligo_9 Endo_1 GW20_Astrocyte_39 Astrocyte_4 Endo_2 Astrocyte_1
692 415 1312 8401 4497 2308
Astrocyte_2 Endo_3 Neuron_37 Dividing_5 IPC_34 IPC_29
159 1621 521 24 35 1
GW18_2_45Oligo Dividing_1 Oligo_8 GW19_2_45Outlier Oligo_6
2 5 4 2 1
DimPlot(seu.q, group.by = 'Bha')

DimPlot(seu.q, group.by = 'subgroups')

Try to predict with the astrocyte Kamath data
astro.ref <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/Macosko_Data/PD_astro.Rds")
# need to make PCA and UMAP
astro.ref <- NormalizeData(astro.ref)
astro.ref <- FindVariableFeatures(astro.ref, selection.method = "vst", nfeatures = 2000)
astro.ref <- ScaleData(astro.ref)
astro.ref <- RunPCA(astro.ref)
astro.ref <- RunUMAP(astro.ref, reduction = "pca", n.neighbors = 205, dims = 1:25)
colnames(astro.ref@meta.data)
Idents(astro.ref) <- "Cell_Subtype"
DefaultAssay(astro.ref) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = astro.ref ,query = seu.q, dims = 1:20)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = astro.ref$Cell_Subtype, k.weight = 10)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$astro.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred', label = TRUE)
table(seu.q$astro.pred)
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, NA)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$astro.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred.thresh', label = TRUE)
table(seu.q$astro.pred.thresh)
# 19986 Astro_VIM_TNFSRF12A no threshold Astro_GLYATL2 14
# 8376 Astro_VIM_TNFSRF12A with 95% threshold
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
Possible predicted in other clusters
clustree(seu.q)

Make the prediction table for high resolution Res 0.8 12 clusters
# AIW002 120 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW120 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw120 <- top.pred.celltype.AIW120[order(top.pred.celltype.AIW120$Var1,-top.pred.celltype.AIW120$Freq),]
row.names(df.top.aiw120) <- NULL
df.top.aiw120$I <- row.names(df.top.aiw120)
# AIW002 60 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW60 <-as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw60 <- top.pred.celltype.AIW60[order(top.pred.celltype.AIW60$Var1,-top.pred.celltype.AIW60$Freq),]
row.names(df.top.aiw60) <- NULL
df.top.aiw60$I <- row.names(df.top.aiw60)
# AST23 165 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AST23 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.AST23 <- top.pred.celltype.AST23[order(top.pred.celltype.AST23$Var1,-top.pred.celltype.AST23$Freq),]
row.names(df.top.AST23) <- NULL
df.top.AST23$I <- row.names(df.top.AST23)
### add in the prediction from brain data Bhaduri midbrain and striatum
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$Bha.mid.stri.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha <- top.pred.celltype.Bha[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha) <- NULL
df.top.Bha$I <- row.names(df.top.Bha)
## these are calculated below
### add in the prediction from brain whole brain data Bhaduri midbrain down sampled
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.8, seu.q$Bha))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha1 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha1 <- top.pred.celltype.Bha1[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha1) <- NULL
df.top.Bha1$I <- row.names(df.top.Bha1)
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table <- merge(pred.table, df.top.Bha1, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha1, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
NA
Look at cluster markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
write.csv(ClusterMarkers,"/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/Glia1AstrocytesClusterMarkers_new.csv")
# for the res 0.6 the largers groups 0 and 1 don't have great markers and none of the markers are really very good.
# I'll rerun with res 0.2
unique(seu.q$RNA_snn_res.0.2)
# still not much better
Check cell type markers with EnrichR
library(enrichR)
setEnrichrSite("Enrichr") # Human genes
# list of all the databases
# libaries with cell types
db <- c('Allen_Brain_Atlas_up','Descartes_Cell_Types_and_Tissue_2021',
'CellMarker_Augmented_2021','Azimuth_Cell_Types_2021')
# enrichr(genes, databases = NULL)
#I'll run the clusters one at a time
N1.c0 <- ClusterMarkers %>% filter(cluster == 5 & avg_log2FC > 0)
genes <- N1.c0$gene
N1.c0.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c0.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[4]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c0.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c0.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c0.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
N1.Er.genes.4 <- N1.c0.Er[[4]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.4
# cluster 0 - Cell type marker library - Brain astrocyte top hit and embryonic astrocytes
# gene list in term: brain astrocyte EFEMP1;NFIA;LIX1;PSAP;KIF21A;S100B;CRYAB;DKK3
# embryo astrocytes SOX2;BEX1;HMGCS1;PTPRZ1;LIX1;S100B;DKK3;ITM2C
# cluster 1 - stem cell pericyte (brain), stelate, astrocyte
# cluster 2 - hypothalmus, endothelial cells, macrophage
# endothelial CSTB;PRELID1;MT1X;CRIP2;RHOC;TMEM141;MT2A;RPS28;CCDC85B;EIF3I;RBP1;ID1;C4ORF3;ID3;PCBD1;MSX1;PPIC
# cluster 3 - smooth muscle cells
# cluster 4 - NK cells, fibroblasts
# NK cells ITGB1;RAB5C;GSTP1;PDCD5;EEF1B2;TGOLN2;SDCBP;MT2A;LDHA;SNRPD2;YWHAQ;ZNF326;TMSB10;CCDC50
# fibroblasts COL3A1;CALD1;COL6A3
# cluster 5 - endothelial cells, NK cells, CD8+
# cluster 6 - stromal cells eurythroblasts, none-neuronal, oligo
# reran and now there are only 5 clusters
# repeat checking
Cluster 0 - astrocytes Cluster 1 - pericyte astrocyte (weak still) Cluster 2 - endothelial Cluster 3 - smooth muscle Cluster 4 - NK/fibroblast Cluster 5 - endothelial Cluster 6 - non- neuronal
VlnPlot(seu.q, features = c("CD44","ITGB1","S100B"), group.by = 'orig.ident')
VlnPlot(seu.ft, features = c("CD44","ITGB1","S100B"), group.by = 'orig.ident')
Check expression of known markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
feature_list = c("MKI67","SOX2","POU5F1","DLX2","PAX6","SOX9","HES1","NES","RBFOX3","MAP2","NCAM1","CD24","GRIA2","GRIN2B","GABBR1","GAD1","GAD2","GABRA1","GABRB2","TH","ALDH1A1","LMX1B","NR4A2","CORIN","CALB1","KCNJ6","CXCR4","ITGA6","SLC1A3","CD44","AQP4","S100B", "PDGFRA","OLIG2","MBP","CLDN11","VIM","VCAM1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list) +RotatedAxis()
PD_poulin = c("TH","SLC6A3","SLC18A2","SOX6","NDNF","SNCG","ALDH1A1","CALB1","TACR2","SLC17A6","SLC32A1","OTX2","GRP","LPL","CCK","VIP")
DoHeatmap(seu.q, features = PD_poulin, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = PD_poulin)+RotatedAxis()
ealryNeur = c("DCX","NEUROD1","TBR1")
proliferation = c("PCNA","MKI67")
neuralstem = c("SOX2","NES","PAX6","MASH1")
feature_list <- c("DCX","NEUROD1","TBR1","PCNA","MKI67","SOX2","NES","PAX6","MASH1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list)+RotatedAxis()
mat_neuron = c("RBFOX3","SYP","DLG45","VAMP1","VAMP2","TUBB3","SYT1","BSN","HOMER1","SLC17A6")
# NeuN is FOX3 - RBFOX3
# PSD95 also SP-90 or DLG4
# VGLUT2 is SLC17A6
DoHeatmap(seu.q, features = mat_neuron, size=3, angle =90, group.bar.height = 0.02)
# cluster 4 also show mature neuron markers
DotPlot(seu.q, features = mat_neuron)+RotatedAxis()
# excitatory neuron markers
ex = c("GRIA2","GRIA1","GRIA4","GRIN1","GRIN2B","GRIN2A","GRIN3A","GRIN3","GRIP1","CAMK2A")
DoHeatmap(seu.q, features = ex, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ex)+RotatedAxis()
# inhibitory neuron markers
inh = c("GAD1","GAD2", "GAT1","PVALB","GABR2","GABR1","GBRR1","GABRB2","GABRB1","GABRB3","GABRA6","GABRA1","GABRA4","TRAK2")
DoHeatmap(seu.q, features = inh, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = inh)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
### glia markers
microglia = c("PTPRC","AIF1","ADGRE1") # ADGRE1 is a microglia marker F4/80, CD45 is PTPRC, gene name IBA1 is AIF1
astolgNPCpromicro = c("GFAP","S100B","SLC1A2","MBP","SOX10","SPP1","DCX","NEUROD1","TBR1","PCNA","MKI67","PTPRC","AIF1","ADGRE1")
# note GLT1 is EAAT2 which is SLC1A2 glutatmate transporter
# epithelial
epi = c("HES1","HES5","SOX2","SOX10","NES","CDH1","NOTCH1") # e-cadherin is CDH1
DoHeatmap(seu.q, features = astolgNPCpromicro, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = astolgNPCpromicro)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
DoHeatmap(seu.q, features = epi, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = epi)+RotatedAxis()
# also add Radial glia marker overlap with Glia and Neurons
features <- c("PTPRC","AIF1","ADGRE1", "VIM", "TNC","PTPRZ1","FAM107A","HOPX","LIFR",
"ITGB5","IL6ST")
DoHeatmap(seu.q, features = features, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = features)+RotatedAxis()
No TH expression
clusters 5 VIM highest, S100 B Cluster 4 Cluster 3 has some cells with high OTX2, NES indicates NPC/Precursors Cluster 2 has some SOX2 and PAX6 indicates NPC, VAMP2 indicating neurons, S100B highest and most - indicates astrocytes, also MBP indicates oligos Cluster 1 Cluster 0
Lable the clusters
Idents(seu.q) <- 'RNA_snn_res.0.2'
#seu.q <- BuildClusterTree(seu.q, reorder = TRUE, reorder.numeric = TRUE)
unique(seu.q$RNA_snn_res.0.2)
cluster.ids <- c("Astrocytes1","Astrocytes2","Precursors","RG1","RG2","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$subgroups <- Idents(seu.q)
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.2', label = TRUE)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'subgroups', repel = TRUE)
# something weird is going on in the order
#saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
Compare the Astrocyte groups and get some markers for sub groups
astro.sub.markers <- FindMarkers(seu.q, ident.1 = "Astrocytes1", ident.2 = "Astrocytes2", only.pos = FALSE)
#top5 <- astro.sub.markers %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = c("PLCG2","PTPRZ1","SNHG25","VCAN","LUM","DCN","S1004A"), size=3, angle =90, group.bar.height = 0.02)
astro2 <- rownames(astro.sub.markers %>% filter(avg_log2FC < 0.05))
DotPlot(seu.q, features = c("PLCG2","PTPRZ1","SNHG25","VCAN","LUM","DCN","S1004A")) + RotatedAxis()
DoHeatmap(seu.q, features = astro2[1:15], size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = astro2[1:15]) +RotatedAxis()
# radial glia subtyping
Idents(seu.q) <- ('subgroups')
rg.sub.markers <- FindMarkers(seu.q, ident.1 = "RG1", ident.2 = "RG2", only.pos = FALSE)
top5.up <- rg.sub.markers %>% top_n(n=10, wt = avg_log2FC)
top5.down <- rg.sub.markers %>% top_n(n=-10, wt = avg_log2FC)
ft <- rownames(top5.up)
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
ft <- rownames(top5.down)
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
npc.markers <- FindMarkers(seu.q, ident.1 = "Precursors", ident.2 = c("Astrocytes2","Astrocytes1"), only.pos = FALSE)
top10.npc <- npc.markers %>% top_n(n=10, wt = avg_log2FC)
npc.markers <- npc.markers %>% filter(avg_log2FC > 0)
dim(npc.markers)
ft <- rownames(top10.npc)
# consider naming precursors as astrocytes3
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
npc.markers.rg <- FindMarkers(seu.q, ident.1 = "Precursors", ident.2 = c("RG2","RG1"), only.pos = TRUE)
top10.npc <- npc.markers.rg %>% top_n(n=10, wt = avg_log2FC)
ft <- rownames(top10.npc)
# consider naming precursors as astrocytes3
DoHeatmap(seu.q, features = ft, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ft) + RotatedAxis()
## these have more differences from RG than Astrocytes
Add subtype gene ids
DimPlot(seu.q, group.by = 'RNA_snn_res.0.2')
cluster.ids <- c("Astrocytes-PLCG2","Astrocytes-DNC","Astrocytes-IGFBP2",
"RG1-CDKN1C","RG2-TYRP1","Endothelial")
#cluster.ids <- c("Astrocytes1","Astrocytes2","Precursors(Astrocytes)","RG1","RG2","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_types <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_types', repel = TRUE)
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
# label with main cell type groups
DimPlot(seu.q, group.by = 'RNA_snn_res.0.2')
cluster.ids <- c("Astrocytes","Astrocytes","Astrocytes",
"RG","RG","Endothelial")
#cluster.ids <- c("Astrocytes1","Astrocytes2","Precursors(Astrocytes)","RG1","RG2","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Type <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Type', repel = TRUE)
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
Label main cell types Res 0.8 predicitons 0 astro 1 Endo/RG/Astro 2 Astrocyte 3 RG/Endo/Astro 4 Astro 5 Astro 6 Epithela, endo, astro, RG 7 Astro 8 Astro 9 Astro/RG/Endo 10 Astro/RG 11 Astro/Neurons 12 Endo/RG.
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.8')
Idents(seu.q) <- 'RNA_snn_res.0.8'
cluster.ids <- c("Astrocytes","Astrocytes-Endo-RG","Astrocytes",
"RG-Endo-Astro","Astro","Astro","Epi-Endo-Astro-RG",
"Astro","Astro","Astro-RG-Endo","Astro-RG","Astro-Neurons","Endo-RG")
cluster.ids <- c("Astrocytes","Astrocytes","Astrocytes",
"Astrocytes","Astrocytes","Astrocytes","Endothelial",
"Astrocytes","Astrocytes","Radial Glia","Radial Glia","Astrocytes","Endothelial")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Type <- Idents(seu.q)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Type', repel = TRUE)

saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia1LabledSeu301102022.RDS")
table(seu.q$Cell_Type)
Astrocytes RG Endothelial Radial Glia
16068 2955 506 471
Quick check the Glial2
# explore filtering
seu <- Glia2
seu
#
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA", "nCount_RNA", "percent.mt"), ncol = 3)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 1000)
VlnPlot(seu, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 500)
VlnPlot(seu, pt.size = 0.10, features = c("nCount_RNA"), y.max = 2000)
# filter more cells
seu.ft <- subset(seu, subset = nFeature_RNA > 250 & nCount_RNA > 250 & nCount_RNA < 10000)
seu.ft
VlnPlot(seu.ft, pt.size = 0.10, features = c("nFeature_RNA"), y.max = 2000)
VlnPlot(seu.ft.glia, features = c("CD44","S100B","ITGB1"))
VlnPlot(seu.ft, features = c("CD44","S100B","ITGB1"), group.by = 'orig.ident')
# both glia populations are similar
Levels seem similar in Glia1 and Glia2
Remove doublets and start to process Glia 2
suppressMessages(require(DoubletFinder))
# filtering out MALAT1 and mitochondrial genes
seu.ft <- seu.ft[!grepl("MALAT1", rownames(seu.ft)), ]
seu.ft <- seu.ft[!grepl("^MT-", rownames(seu.ft)), ]
# like in the tutorial I'm following MALAT1 is the top most expressed gene. The top genes are a lot of MT and Ribosomal genes
seu.ft[["percent.rb"]] <- PercentageFeatureSet(seu.ft, pattern = "^RP")
seu.d = NormalizeData(seu.ft)
seu.d = FindVariableFeatures(seu.d, verbose = F)
seu.d = ScaleData(seu.d, vars.to.regress = c("nFeature_RNA", "percent.mt"),
verbose = F)
seu.d = RunPCA(seu.d, verbose = F, npcs = 15)
seu.d = RunUMAP(seu.d, dims = 1:10, verbose = F)
nExp <- round(ncol(seu.d) * 0.08) # expect more doublets because there is a lot more cells
seu.d <- doubletFinder_v3(seu.d, pN = 0.25, pK = 0.09, nExp = nExp, PCs = 1:10)
# the memory limit is reached here - I could run on compute canada
# For now I'll downsample
# this works
# name of the DF prediction can change, so extract the correct column name.
DF.name = colnames(seu.d@meta.data)[grepl("DF.classification", colnames(seu.d@meta.data))]
cowplot::plot_grid(ncol = 2, DimPlot(seu.d, group.by = "orig.ident") + NoAxes(),
DimPlot(seu.d, group.by = DF.name) + NoAxes())
VlnPlot(seu.d, features = "nFeature_RNA", group.by = DF.name, pt.size = 0.1)
seu.d <- seu.d[, seu.d@meta.data[, DF.name]== "Singlet"]
dim(seu.d)
dim(seu.ft)
Cluster
seu <- NormalizeData(seu.d, normalization.method = "LogNormalize", scale.factor = 10000)
seu <- FindVariableFeatures(seu, selection.method = "vst", nfeatures = 2000)
seu <- ScaleData(seu)
seu <- RunPCA(seu)
seu <- RunUMAP(seu, reduction = "pca", n.neighbors = 25, dims = 1:30)
DimPlot(seu, reduction = "umap")
seu.q <- FindNeighbors(seu, dims = 1:25, k.param = 25)
seu.q <- FindClusters(seu.q, resolution = c(0,0.05,0.2,0.4,0.5,0.6,0.8))
library(clustree)
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.05')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.1')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.2')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.4')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.6')
DimPlot(seu.q, reduction = "umap", group.by = 'RNA_snn_res.0.8')
clustree(seu.q)
# 0.4 is likely the best annotate subgroups
Predict cell types
# SNCA and control midbrain organoids 165 days in culture
MBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AST23_BrainComm/MBOclusters_names29072021.rds")
# Midbrain AIW002 120 days in culture
AIWMBO <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio120days/MOintegratedClusterK123res0.8.names_nov16_2021")
# Midbrain AIW002 60 days in culture
AIW60 <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/AIWtrio60days/AWI002ParkinKOPinkKO60days_labels_14052022.rds")
#first predict with the MBO data
Idents(MBO) <- "cluster_labels"
DefaultAssay(MBO) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = MBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = MBO$cluster_labels)
seu.q <- AddMetaData(seu.q, metadata = predictions)
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$MBOAST23.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'MBOAST23.pred', label = TRUE)
# see how accurate the predictions are
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "None")
Idents(seu.q) <- 'predicted.id'
seu.q$MBOAST23.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'predicted.id', label = TRUE)
table(seu.q$MBOAST23.pred)
table(seu.q$MBOAST23.thresh)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.4, seu.q$MBOAST23.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# clusters don't break up by the predicted cell types
############ another predictions now using the AIW organoids
Idents(AIWMBO) <- "res08names"
DefaultAssay(AIWMBO) <- "RNA"
anchors <- FindTransferAnchors(reference = AIWMBO ,query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIWMBO$res08names)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW120.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW120.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.4, seu.q$MBOAIW.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# see how accurate the predictions are
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "None")
Idents(seu.q) <- 'predicted.id'
seu.q$AIW120.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW120.thresh', label = TRUE)
table(seu.q$AIW120.pred)
table(seu.q$AIW120.thresh)
# the predicted cell types make more sense from the AIW002 organoid
# now predict with the AIW002 60 days organoid
Idents(AIW60) <- "cluster.ids"
DefaultAssay(AIW60) <- "RNA"
anchors <- FindTransferAnchors(reference = AIW60, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = AIW60$cluster.ids)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$AIW60.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.pred', label = TRUE)
## check the proportion of cell types predicted in each cluster
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.4, seu.q$AIW60.pred))
t.lables$Freq <- as.double(t.lables$Freq)
# try bar chart
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity")
# see how accurate the predictions are
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "None")
Idents(seu.q) <- 'predicted.id'
seu.q$AIW60.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'AIW60.thresh', label = TRUE)
table(seu.q$AIW60.pred)
table(seu.q$AIW60.thresh)
# most of the cells are predicted as NPCs in many populations
# save with predictions so far
#saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
See how many cells are predicted as astrocytes with the threshold
DAsubtypes <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/Macosko_Data/DAsubgroups_processed.Rds")
Idents(astro.ref) <- "Cell_Subtype"
DefaultAssay(astro.ref) <- "RNA"
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = DAsubtypes ,query = seu.q, dims = 1:20)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = astro.ref$Cell_Subtype, k.weight = 10)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$astro.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred', label = TRUE)
table(seu.q$astro.pred)
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$astro.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'astro.pred.thresh', label = TRUE)
table(seu.q$astro.pred.thresh)
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
# a lot of these cells are also getting labelled as astrocytes
Do these get labelled as DA neurons too???
seu.q <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
DAsubtypes <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/Macosko_Data/DAsubgroups_processed.Rds")
Idents(DAsubtypes) <- "Cell_Subtype"
da.ref <- subset(DAsubtypes, downsample = 500)
# find the reference anchors
print("finding reference anchors")
anchors <- FindTransferAnchors(reference = da.ref, query = seu.q, dims = 1:20)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = da.ref$Cell_Subtype, k.weight = 10)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
# add new dataslot for MBO predicted ID to make the next prediction
seu.q$da.pred <- Idents(seu.q)
DimPlot(seu.q, group.by = 'da.pred', label = TRUE)
table(seu.q$da.pred)
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$da.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'da.pred.thresh', label = TRUE)
table(seu.q$da.pred.thresh)
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$da.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.da$Freq),]
row.names(df.top.astro) <- NULL
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()
top.pred.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.astro[order(top.pred.astro$Var1,-top.pred.astro$Freq),]
row.names(df.top.astro) <- NULL
# after thresholding very few cells are predicted as neurons
{redicte with the brain scRNAseq
pathway <- "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/"
seu.q <- readRDS(paste(pathway,"Glia2LabledSeu03102022.RDS",sep = ""))
# midbrain and striatum
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1841 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 881 anchors
Filtering anchors
Retained 303 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_4 Endo_11 Endo_4 Endo_7 Neuron_2 Neuron_37 Neuron_98
5810 4 506 186 41 6 396
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
Astrocyte_4 Neuron_2 Neuron_98 Endo_4 Endo_7 Endo_11 Neuron_37
5810 41 396 506 186 4 6
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh', label = TRUE)

table(seu.q$Bha.mid.pred.thresh)
none Astrocyte_4
6942 7
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh')

# read in the reference dataset
# whole brain Bhaduri down sampled
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_downsample.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1844 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 1726 anchors
Filtering anchors
Retained 509 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Astrocyte_1 Astrocyte_2 Astrocyte_4 Astrocyte_6 Dividing_21
480 203 1310 2 136
Dividing_3 Endo_1 Endo_11 Endo_2 Endo_3
7 85 19 522 1
Endo_4 Endo_7 Endo_8 GW19_2_45Outlier GW19_Astrocyte_35
2 3 125 1604 1
GW20_Astrocyte_39 GW22both_RG_23 Interneuron_7 IPC_29 IPC_34
138 252 192 747 350
Microglia_6 Neuron_5 Neuron_93 Oligo_12 Oligo_9
5 6 253 11 1
RG_1 RG_11 RG_6
492 1 1
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.pred <- Idents(seu.q)
print(table(seu.q$Bha.pred))
Astrocyte_2 GW19_2_45Outlier Endo_8 IPC_29 IPC_34
203 1604 125 747 350
Interneuron_7 Dividing_21 Astrocyte_4 GW20_Astrocyte_39 Astrocyte_1
192 136 1310 138 480
Neuron_93 GW22both_RG_23 Endo_2 Endo_11 Endo_1
253 252 522 19 85
RG_1 Neuron_5 Endo_3 Microglia_6 Dividing_3
492 6 1 5 7
Oligo_12 RG_6 Endo_7 Endo_4 Astrocyte_6
11 1 3 2 2
GW19_Astrocyte_35 Oligo_9 RG_11
1 1 1
DimPlot(seu.q, group.by = 'Bha.pred')
DimPlot(seu.q, group.by = 'subgroups')
Compare predictions - make a predictions table
# AIW002 120 days predictions - take the thresholded options
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$AIW120.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW120 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw120 <- top.pred.celltype.AIW120[order(top.pred.celltype.AIW120$Var1,-top.pred.celltype.AIW120$Freq),]
row.names(df.top.aiw120) <- NULL
df.top.aiw120$I <- row.names(df.top.aiw120)
# AIW002 60 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$AIW60.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AIW60 <-as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.aiw60 <- top.pred.celltype.AIW60[order(top.pred.celltype.AIW60$Var1,-top.pred.celltype.AIW60$Freq),]
row.names(df.top.aiw60) <- NULL
df.top.aiw60$I <- row.names(df.top.aiw60)
# AST23 165 days predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$MBOAST23.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.AST23 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.AST23 <- top.pred.celltype.AST23[order(top.pred.celltype.AST23$Var1,-top.pred.celltype.AST23$Freq),]
row.names(df.top.AST23) <- NULL
df.top.AST23$I <- row.names(df.top.AST23)
# add the threshold Astro predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$astro.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.astro <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.astro <- top.pred.celltype.astro[order(top.pred.celltype.astro$Var1,-top.pred.celltype.astro$Freq),]
row.names(df.top.astro) <- NULL
df.top.astro$I <- row.names(df.top.astro)
# add the neurons predictions
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$da.pred.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.da <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.da <- top.pred.celltype.da[order(top.pred.celltype.da$Var1,-top.pred.celltype.da$Freq),]
row.names(df.top.da) <- NULL
df.top.da$I <- row.names(df.top.da)
### add in the prediction from brain data Bhaduri midbrain and striatum
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha.mid.stri.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha <- top.pred.celltype.Bha[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha) <- NULL
df.top.Bha$I <- row.names(df.top.Bha)
## these are calculated below
### add in the prediction from brain whole brain data Bhaduri midbrain down sampled
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$Bha.pred))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype.Bha1 <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top.Bha1 <- top.pred.celltype.Bha1[order(top.pred.celltype.Bha$Var1,-top.pred.celltype.Bha$Freq),]
row.names(df.top.Bha1) <- NULL
df.top.Bha1$I <- row.names(df.top.Bha1)
pred.table <- merge(df.top.AST23, df.top.aiw60, by = 'I', all = TRUE)
pred.table <- merge(pred.table, df.top.aiw120, by = 'I')
pred.table <- merge(pred.table, df.top.Bha, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table <- merge(pred.table, df.top.Bha1, by = 'I')
Warning in merge.data.frame(pred.table, df.top.Bha1, by = "I") :
column names ‘Var1.x’, ‘Var2.x’, ‘Freq.x’, ‘Var1.y’, ‘Var2.y’, ‘Freq.y’ are duplicated in the result
pred.table
NA
NA
NA
Predicted cluster annotations 0 Unknown/ NPC 1 RG 2 astro 3 RG 4 neurons 5 RG
Predict from developing cortex
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1389 features as input.
Projecting cell embeddings
Finding neighborhoods
Error in base::options(...future.oldOptions) : invalid argument
The dev cortex doesn’t predict Glia 2 well Try the developing forebrain
DefaultAssay(seu.r) <- 'RNA'
# clusters has subgroups
# levels are main cell groups
Idents(seu.r) <- "Level1"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1851 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 585 anchors
Filtering anchors
Retained 250 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$Level1)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
EarlyInhibitory Glioblast NeuralProgenitor RadialGlia VLMC
7 2893 1299 2630 120
Idents(seu.q) <- 'predicted.id'
seu.q$fb.pred <- Idents(seu.q)
print(table(seu.q$fb.pred))
RadialGlia Glioblast NeuralProgenitor EarlyInhibitory VLMC
2630 2893 1299 7 120
DimPlot(seu.q, group.by = 'fb.pred')

# add the threshold
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.80, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$fb.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'fb.thresh', label = TRUE)

table(seu.q$fb.thresh)
RadialGlia none VLMC NeuralProgenitor
1028 5659 113 149
# make the tables
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$fb.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(2, Freq))
df.top <- top.pred.celltype[order(top.pred.celltype$Var1,-top.pred.celltype$Freq),]
row.names(df.top) <- NULL
df.top$I <- row.names(df.top)
#VLMC is vascular and leptomeninges
# almost everything is predicted as 'none' when theshold is .95
# run again with lower threshold 0.8 and many are predicted as RG
# try predicting with the cluster labels
# later I can subset cell types for the reference data
Idents(seu.r) <- "Clusters"
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
Performing PCA on the provided reference using 1851 features as input.
Projecting cell embeddings
Finding neighborhoods
Finding anchors
Found 585 anchors
Filtering anchors
Retained 250 anchors
print("getting predictions")
[1] "getting predictions"
predictions <- TransferData(anchorset = anchors, refdata = seu.r$Clusters)
Finding integration vectors
Finding integration vector weights
0% 10 20 30 40 50 60 70 80 90 100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Predicting cell labels
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Excitatory neurons possibly midbrain Glioblast/Pre-OPC
1947 4
OPCs Radial Glia VLMC primed?
1 2931
Radial glia/Glioblast/Forebrain progenitor EMX1 Striatum/Cortical neurons
75 1871
VLMCs
120
Idents(seu.q) <- 'predicted.id'
seu.q$fb.sub.pred <- Idents(seu.q)
print(table(seu.q$fb.sub.pred))
Radial Glia VLMC primed? Excitatory neurons possibly midbrain
2931 1947
Striatum/Cortical neurons Radial glia/Glioblast/Forebrain progenitor EMX1
1871 75
VLMCs OPCs
120 1
Glioblast/Pre-OPC
4
DimPlot(seu.q, group.by = 'fb.sub.pred')

# add the threshold
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.80, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$fb.sub.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'fb.sub.thresh', label = TRUE)

table(seu.q$fb.sub.thresh)
Radial Glia VLMC primed? none
1015 5727
VLMCs Excitatory neurons possibly midbrain
113 94
t.lables <- as.data.frame(table(seu.q$RNA_snn_res.0.2, seu.q$fb.sub.thresh))
t.lables$Freq <- as.double(t.lables$Freq)
ggplot(t.lables, aes(y = Freq, x = Var1, fill = Var2)) + geom_bar(position = "stack", stat= "identity") + RotatedAxis()

top.pred.celltype <- as.data.frame(t.lables %>% group_by(Var1) %>% top_n(4, Freq))
df.top <- top.pred.celltype[order(top.pred.celltype$Var1,-top.pred.celltype$Freq),]
row.names(df.top) <- NULL
df.top$I <- row.names(df.top)
df.top.ft <- df.top %>% filter(Freq > 0)
Predictions with thresholds:
0 - RG 1 - RG 2 - none 3 - RG 4 - Neural precursor 5 - VLMC (vascular and leptomeninges)
Look at gene lists with known markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
# many cell types list
feature_list = c("MKI67","SOX2","POU5F1","DLX2","PAX6","SOX9","HES1","NES","RBFOX3","MAP2","NCAM1","CD24","GRIA2","GRIN2B","GABBR1","GAD1","GAD2","GABRA1","GABRB2","TH","ALDH1A1","LMX1B","NR4A2","CORIN","CALB1","KCNJ6","CXCR4","ITGA6","SLC1A3","CD44","AQP4","S100B", "PDGFRA","OLIG2","MBP","CLDN11","VIM","VCAM1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list) +RotatedAxis()
# Dopaminergic markers
PD_poulin = c("TH","SLC6A3","SLC18A2","SOX6","NDNF","SNCG","ALDH1A1","CALB1","TACR2","SLC17A6","SLC32A1","OTX2","GRP","LPL","CCK","VIP")
DoHeatmap(seu.q, features = PD_poulin, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = PD_poulin)+RotatedAxis()
ealryNeur = c("DCX","NEUROD1","TBR1")
proliferation = c("PCNA","MKI67")
neuralstem = c("SOX2","NES","PAX6","MASH1")
feature_list <- c("DCX","NEUROD1","TBR1","PCNA","MKI67","SOX2","NES","PAX6","MASH1")
DoHeatmap(seu.q, features = feature_list, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = feature_list)+RotatedAxis()
mat_neuron = c("RBFOX3","SYP","DLG45","VAMP1","VAMP2","TUBB3","SYT1","BSN","HOMER1","SLC17A6")
# NeuN is FOX3 - RBFOX3
# PSD95 also SP-90 or DLG4
# VGLUT2 is SLC17A6
DoHeatmap(seu.q, features = mat_neuron, size=3, angle =90, group.bar.height = 0.02)
# cluster 4 also show mature neuron markers
DotPlot(seu.q, features = mat_neuron)+RotatedAxis()
# excitatory neuron markers
ex = c("GRIA2","GRIA1","GRIA4","GRIN1","GRIN2B","GRIN2A","GRIN3A","GRIN3","GRIP1","CAMK2A")
DoHeatmap(seu.q, features = ex, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = ex)+RotatedAxis()
# inhibitory neuron markers
inh = c("GAD1","GAD2", "GAT1","PVALB","GABR2","GABR1","GBRR1","GABRB2","GABRB1","GABRB3","GABRA6","GABRA1","GABRA4","TRAK2")
DoHeatmap(seu.q, features = inh, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = inh)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
### glia markers
microglia = c("PTPRC","AIF1","ADGRE1") # ADGRE1 is a microglia marker F4/80, CD45 is PTPRC, gene name IBA1 is AIF1
astolgNPCpromicro = c("GFAP","S100B","SLC1A2","MBP","SOX10","SPP1","DCX","NEUROD1","TBR1","PCNA","MKI67","PTPRC","AIF1","ADGRE1")
# note GLT1 is EAAT2 which is SLC1A2 glutatmate transporter
# epithelial
epi = c("HES1","HES5","SOX2","SOX10","NES","CDH1","NOTCH1") # e-cadherin is CDH1
DoHeatmap(seu.q, features = astolgNPCpromicro, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = astolgNPCpromicro)+RotatedAxis()
# cluster 4 is more excitatory than inhbitory but neither marker set has much expression
DoHeatmap(seu.q, features = epi, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = epi)+RotatedAxis()
# also add Radial glia marker overlap with Glia and Neurons
features <- c("PTPRC","AIF1","ADGRE1", "VIM", "TNC","PTPRZ1","FAM107A","HOPX","LIFR",
"ITGB5","IL6ST")
DoHeatmap(seu.q, features = features, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = features)+RotatedAxis()
# radial glia markers
rg <- c("VIM","NES","PAX6","HES1","EAAT1","NCAD1","SOX2","FABP7")
DoHeatmap(seu.q, features = rg, size=3, angle =90, group.bar.height = 0.02)
DotPlot(seu.q, features = rg)+RotatedAxis()
# NPC and radial glia are very similar
Marker expression predictions Cluster 0 - unknown Cluster 1 - RG Cluster 2 - unknown Cluster 3 - RG cluster 4 - immature neurons Cluster 5 - RG, opc
Check the levels of RNA in each cluster
VlnPlot(seu.q, features = "nFeature_RNA")
Cluster 0 and 2 have fewer sequences than other groups and thus no markers Possibly remove these is they don’t come up with some markers
Find cluster markers
Idents(seu.q) <- 'RNA_snn_res.0.2'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
#write.csv(ClusterMarkers,"/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/Glia2RGClusterMarkers_new.csv")
Idents(seu.q) <- 'RNA_snn_res.0.1'
ClusterMarkers <- FindAllMarkers(seu.q, only.pos = TRUE)
top5 <- ClusterMarkers %>% group_by(cluster) %>% top_n(n=5, wt = avg_log2FC)
DoHeatmap(seu.q, features = top5$gene, size=3, angle =90, group.bar.height = 0.02)
Markers of 2 are matching with 5 possibly merge these together Cluster 0 markers don’t look up regulated but the list is long
Look at the libraries
library(enrichR)
setEnrichrSite("Enrichr") # Human genes
# list of all the databases
# libaries with cell types
db <- c('Descartes_Cell_Types_and_Tissue_2021',
'CellMarker_Augmented_2021','Azimuth_Cell_Types_2021')
# enrichr(genes, databases = NULL)
#I'll run the clusters one at a time
N1.c0 <- ClusterMarkers %>% filter(cluster == 0 & avg_log2FC > 0)
genes <- N1.c0$gene
N1.c0.Er <- enrichr(genes, databases = db)
plotEnrich(N1.c0.Er[[1]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[2]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
plotEnrich(N1.c0.Er[[3]], showTerms = 20, numChar = 40, y = "Count", orderBy = "P.value")
N1.Er.genes.1 <- N1.c0.Er[[1]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.1
N1.Er.genes.2 <- N1.c0.Er[[2]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.2
N1.Er.genes.3 <- N1.c0.Er[[3]] %>% select(Term, Genes, Combined.Score)
N1.Er.genes.3
The adult brain doesn’t predict radial glia - there are radial glia but I believe these are different from the ‘neuroblast’ type radial glia
I will use a developing brain reference
DimPlot(seu.q)
seu.q <- readRDS(paste(pathway,"Glia2LabledSeu03102022.RDS",sep = ""))
# midbrain and striatum
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_midbrain_striatum.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.stri.pred <- Idents(seu.q)
print(table(seu.q$Bha.mid.stri.pred))
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh', label = TRUE)
table(seu.q$Bha.mid.pred.thresh)
DimPlot(seu.q, group.by = 'Bha.mid.pred.thresh')
# read in the reference dataset
# whole brain Bhaduri down sampled
seu.r <- readRDS("/Users/rhalenathomas/Documents/Data/scRNAseq/PublicData/Bhaduri_wholeBrain/Bhaduri_downsample.RDS")
Idents(seu.r) <- "cell_cluster"
# find the reference anchors
anchors <- FindTransferAnchors(reference = seu.r, query = seu.q, dims = 1:25)
print("getting predictions")
predictions <- TransferData(anchorset = anchors, refdata = seu.r$cell_cluster)
seu.q <- AddMetaData(seu.q, metadata = predictions)
print(table(seu.q$predicted.id))
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.pred <- Idents(seu.q)
print(table(seu.q$Bha.pred))
DimPlot(seu.q, group.by = 'Bha.pred')
DimPlot(seu.q, group.by = 'subgroups')
seu.q$predicted.id <- ifelse(seu.q$prediction.score.max > 0.95, seu.q$predicted.id, "none")
Idents(seu.q) <- 'predicted.id'
seu.q$Bha.mid.pred.thresh <- Idents(seu.q)
DimPlot(seu.q, group.by = 'Bha.pred.thresh', label = TRUE)
table(seu.q$Bha.mid.pred.thresh)
DimPlot(seu.q, group.by = 'Bha.pred.thresh')
Add some cell type annotations
Idents(seu.q) <- 'RNA_snn_res.0.2'
cluster.ids <- c("Glia1","RG1","Glia2","RG2","NeuronsImmature","RG3")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$subgroups <- Idents(seu.q)
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.2', label = TRUE)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'subgroups', repel = TRUE)
# save file
saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
Main cell groups
Idents(seu.q) <- 'RNA_snn_res.0.2'
cluster.ids <- c("Radial Glia","Radial Glia","Radial Glia","Radial Glia","NPC","Other")
names(cluster.ids) <- levels(seu.q)
seu.q <- RenameIdents(seu.q, cluster.ids)
seu.q$Cell_Types <- Idents(seu.q)
#DimPlot(seu.q, group.by = 'RNA_snn_res.0.2', label = TRUE)
DimPlot(seu.q, reduction = "umap", label = TRUE, group.by = 'Cell_Types', repel = TRUE)

saveRDS(seu.q, "/Users/rhalenathomas/Documents/Data/scRNAseq/PhenoID/scRNAseqSorted/objs/Glia2LabledSeu03102022.RDS")
Proportions of cell types
table(seu.q$Cell_Types)
dim(seu.q)
prp <- as.data.frame(table(seu.q$Cell_Types))
prp
prp$prop <- prp$Freq/sum(prp$Freq)*100
prp$Sample <- 'RadialGlia'
prp
I’ll calculate the proportions for each cell type and make a table or plot in the comparison workbook.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQpTaW5nbGUgY2VsbCBzZXEgYWZ0ZXIgc29ydGluZyBmb3IgUGhlbm9JRAoKc2FtcGxlMSA9IE5ldXJvbnMxCnNhbXBsZTIgPSBOZXVyb25zMgpzYW1wbGUzID0gR2xpYTEgLSBBc3Ryb2N5dGVzIChDRDQ0KykKc2FtcGxlNCA9IEdsaWEyIC0gUmFkaWFsIEdsaWEgKENENDQtKQoKSW4gSFBDIEkgaGF2ZSBydW4gc3RlcHMgb2Ygc2NybmFib3ggKGN1c3RvbSBwaXBlbGluZSBpbiBwcm9ncmVzcykKMS4gQ2VsbCBSYW5nZXIgZm9yIGZlYXR1cmUgc2VxCjIuIENyZWF0ZSBTZXVyYXQgT2JqZWN0cyAKMy4gQXBwbHkgbWluaW11bSBmaWx0ZXJpbmcgYW5kIGNhbGN1bGF0ZSBwZXJjZW50IG1pdG9jaG9uZHJpYS4KCkkgaGF2ZSB0ZWNobmljYWwgMyByZXBsaWNhdGVzIHdpdGggaGFzaHRhZyBsYWJlbHMgYXQgdGhpcyBwb2ludCBJIGhhdmVuJ3QgeWV0IGRlbXVsdGlwbGV4IHRoZSBoYXNodGFncy4gVGhlIGRhdGEgaGVyZSB3aWxsIGJlIHRyZWF0ZWQgYXMgb25lIHNhbXBsZS4gIEkgc29ydGVkIHRocmVlIHNlcGFyYXRlIHNhbXBsZXMgYW5kIHBvb2xlZCB0aGVtIHRvZ2V0aGVyLiAKCgpgYGB7cn0KIyBzZXQgdXAgdGhlIGVudmlyb25tZW50CgpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShkcGx5cikKbGlicmFyeShNYXRyaXgpCmxpYnJhcnkoZ2dwbG90MikKCiNybShsaXN0ID0gbHMoKSkKCgpgYGAKCgpSZWFkIGluIHRoZSBzZXVyYXQgb2JqZWN0cyBtYWRlIGluIGNvbXB1dGUgY2FuYWRhCgpgYGB7cn0KCiMgdGhpcyBzZWVtcyB0byBuZXZlciBsb2FkIEknbGwgdXNlIHN0ZXAgMyBvdXRwdXQgdGhhdCBoYXMgc29tZSBmaWx0ZXJpbmcgCiMgbkZlYXR1cmVfUk5BID4gMTgwIGFuZCBwZXJjZW50Lm10IDwgMjUKCnBhdGh3YXkgPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy8iCgpOZXVyb25zMSA8LSByZWFkUkRTKHBhc3RlKHBhdGh3YXksInNldTEucmRzIixzZXAgPSAiIikpCk5ldXJvbnMyIDwtIHJlYWRSRFMocGFzdGUocGF0aHdheSwic2V1Mi5yZHMiLHNlcCA9ICIiKSkKR2xpYTEgPC0gcmVhZFJEUyhwYXN0ZShwYXRod2F5LCJzZXUzLnJkcyIsc2VwID0gIiIpKQpHbGlhMiA8LSByZWFkUkRTKHBhc3RlKHBhdGh3YXksInNldTQucmRzIixzZXAgPSAiIikpCgpOZXVyb25zMQpOZXVyb25zMgpHbGlhMQpHbGlhMgoKCmBgYAoKCkhhdmUgYSBsb29rIGF0IHRoZSBvYmplY3RzIHRoYXQgYWxyZWFkeSBoYXZlIHNvbWUgZmlsdGVyaW5nCgoKU2VlIHRoZSB2aW9saW4gcGxvdHMgCgpgYGB7cn0KClZsblBsb3QoTmV1cm9uczEsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgbmNvbCA9IDMpCgpWbG5QbG90KE5ldXJvbnMxLCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiKSwgeS5tYXggPSA1MDApClZsblBsb3QoTmV1cm9uczEsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5Db3VudF9STkEiKSwgeS5tYXggPSAyMDAwKQoKCiMgZmlsdGVyIG1vcmUgY2VsbHMKCk5ldXJvbjEuZnQgPC0gc3Vic2V0KE5ldXJvbnMxLCBzdWJzZXQgPSBuRmVhdHVyZV9STkEgPiAyNTAgJiBuQ291bnRfUk5BID4gMjUwICYgbkNvdW50X1JOQSA8IDEwMDAwKSAKTmV1cm9uMS5mdAoKIyAzMzU0MSBmZWF0dXJlcyBhY3Jvc3MgMTgzMyBzYW1wbGVzCgoKYGBgCgoKRmlsdGVycyBvdXQgc3BlY2lmaWMgZ2VuZXMKCmBgYHtyfQoKIyBmaWx0ZXIgb3V0IE1BTEFUMSBzdXBlciBoaWdoIGV4cHJlc3Npb24KCnNldS5mdCA8LSBzZXVbIWdyZXBsKCJNQUxBVDEiLCByb3duYW1lcyhzZXUpKSwgXQpzZXUuZnQgPC0gc2V1LmZ0WyFncmVwbCgiXk1ULSIsIHJvd25hbWVzKHNldS5mdCkpLCBdCgojIHRoaXMgZmlsdGVyZWQgb2JqZWN0IG1pZ2h0IGNsdXN0ZXIgZGlmZmVyZW50bHkKCgpgYGAKClBDQSBhbmQgVU1BUAoKYGBge3J9CgpzZXUucSA8LSBOb3JtYWxpemVEYXRhKHNldS5mdCwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCnNldS5xIDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldS5xLCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCnNldS5xIDwtIFNjYWxlRGF0YShzZXUucSkKc2V1LnEgPC0gUnVuUENBKHNldS5xKQpzZXUucSA8LSBSdW5VTUFQKHNldS5xLCByZWR1Y3Rpb24gPSAicGNhIiwgbi5uZWlnaGJvcnMgPSAyNSwgZGltcyA9IDE6MzAsIG1pbi5kaXN0ID0gMC4yNSwgc3ByZWFkID0gMikKCmBgYAoKClRyeSB0byBmaW5kIGRvdWJsZXRzIHdpdGggZG91YmxldCBmaW5kZXIKCmBgYHtyfQpyZW1vdGVzOjppbnN0YWxsX2dpdGh1YignY2hyaXMtbWNnaW5uaXMtdWNzZi9Eb3VibGV0RmluZGVyJykKc3VwcHJlc3NNZXNzYWdlcyhyZXF1aXJlKERvdWJsZXRGaW5kZXIpKQoKCmBgYAoKYGBge3J9CgpzZXUuZCA9IEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldS5xLCB2ZXJib3NlID0gRikKc2V1LmQgPSBTY2FsZURhdGEoc2V1LmQsIHZhcnMudG8ucmVncmVzcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJwZXJjZW50Lm10IiksCiAgICB2ZXJib3NlID0gRikKc2V1LmQgPSBSdW5QQ0Eoc2V1LmQsIHZlcmJvc2UgPSBGLCBucGNzID0gMjApCnNldS5kID0gUnVuVU1BUChzZXUuZCwgZGltcyA9IDE6MTAsIHZlcmJvc2UgPSBGKQoKbkV4cCA8LSByb3VuZChuY29sKHNldS5kKSAqIDAuMDYpICAjIGV4cGVjdCA2JSBkb3VibGV0cwpzZXUuZCA8LSBkb3VibGV0RmluZGVyX3YzKHNldS5kLCBwTiA9IDAuMjUsIHBLID0gMC4wOSwgbkV4cCA9IG5FeHAsIFBDcyA9IDE6MTApCgoKIyBuYW1lIG9mIHRoZSBERiBwcmVkaWN0aW9uIGNhbiBjaGFuZ2UsIHNvIGV4dHJhY3QgdGhlIGNvcnJlY3QgY29sdW1uIG5hbWUuCkRGLm5hbWUgPSBjb2xuYW1lcyhzZXUuZEBtZXRhLmRhdGEpW2dyZXBsKCJERi5jbGFzc2lmaWNhdGlvbiIsIGNvbG5hbWVzKHNldS5kQG1ldGEuZGF0YSkpXQoKCgpjb3dwbG90OjpwbG90X2dyaWQobmNvbCA9IDIsIERpbVBsb3Qoc2V1LmQsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKSArIE5vQXhlcygpLAogICAgRGltUGxvdChzZXUuZCwgZ3JvdXAuYnkgPSBERi5uYW1lKSArIE5vQXhlcygpKQoKCmBgYAoKRG8gdGhlIGRvdWJsZSBjZWxscyBoYXZlIG1vcmUgZ2VuZXMgdGhhbiB0aGUgc2luZ2xldD8/CgpgYGB7cn0KClZsblBsb3Qoc2V1LmQsIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIGdyb3VwLmJ5ID0gREYubmFtZSwgcHQuc2l6ZSA9IDAuMSkKIyB5ZXMgCgpgYGAKClJlbW92ZSB0aGUgZG91YmxldHMKCmBgYHtyfQoKc2V1LmQgPC0gc2V1LmRbLCBzZXUuZEBtZXRhLmRhdGFbLCBERi5uYW1lXT09ICJTaW5nbGV0Il0KZGltKHNldS5kKQpkaW0oc2V1KQoKIyByZW1vdmVkIGFib3V0IDEwMCBjZWxscwoKCmBgYAoKClJ1biBjbHVzdGVyaW5nCgpgYGB7cn0KCnNldS5xIDwtIEZpbmROZWlnaGJvcnMoc2V1LmQsIGRpbXMgPSAxOjI1LCBrLnBhcmFtID0gNDMpCnNldS5xIDwtIEZpbmRDbHVzdGVycyhzZXUucSwgcmVzb2x1dGlvbiA9IGMoMCwwLjIsMC40LDAuNikpCnNldS5xIDwtIEZpbmRDbHVzdGVycyhzZXUucSwgcmVzb2x1dGlvbiA9IGMoMS4yKSkKCmxpYnJhcnkoY2x1c3RyZWUpCmNsdXN0cmVlKHNldS5xLCBwcmVmaXggPSAiUk5BX3Nubl9yZXMuIikKRGltUGxvdChzZXUucSkKCmBgYAoKTG9vayBhdCB0aGUgcHJlZGljdGlvbnMgb2YgY2VsbCB0eXBlcyBpbiBzZXVyYXQgbGFiZWwgdHJhbnNmZXIKCjMgb3JnYW5vaWQgZGF0YXNldHMKCgpgYGB7cn0KCgojIFNOQ0EgYW5kIGNvbnRyb2wgbWlkYnJhaW4gb3JnYW5vaWRzIDE2NSBkYXlzIGluIGN1bHR1cmUKTUJPIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FTVDIzX0JyYWluQ29tbS9NQk9jbHVzdGVyc19uYW1lczI5MDcyMDIxLnJkcyIpCgojIE1pZGJyYWluICBBSVcwMDIgMTIwIGRheXMgaW4gY3VsdHVyZQpBSVdNQk8gPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQUlXdHJpbzEyMGRheXMvTU9pbnRlZ3JhdGVkQ2x1c3RlcksxMjNyZXMwLjgubmFtZXNfbm92MTZfMjAyMSIpCgojIE1pZGJyYWluIEFJVzAwMiA2MCBkYXlzIGluIGN1bHR1cmUKCkFJVzYwIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FJV3RyaW82MGRheXMvQVdJMDAyUGFya2luS09QaW5rS082MGRheXNfbGFiZWxzXzE0MDUyMDIyLnJkcyIpCgoKIyBxdWVyeQojc2V1LnEgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL05ldXJvbnNGaWx0ZXJlZFNldTI4MDkyMDIyLlJEUyIpCgoKI2ZpcnN0IHByZWRpY3Qgd2l0aCB0aGUgTUJPIGRhdGEKSWRlbnRzKE1CTykgPC0gImNsdXN0ZXJfbGFiZWxzIgpEZWZhdWx0QXNzYXkoTUJPKSA8LSAiUk5BIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwpwcmludCgiZmluZGluZyByZWZlcmVuY2UgYW5jaG9ycyIpCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBNQk8gLHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gTUJPJGNsdXN0ZXJfbGFiZWxzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRNQk9BU1QyMy5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnTUJPQVNUMjMucHJlZCcsIGxhYmVsID0gVFJVRSkKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJHByZWRpY3RlZC5pZCkpCnByLnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUocHJvcC50YWJsZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJHByZWRpY3RlZC5pZCkpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQoKCiMgdHJ5IGJhciBjaGFydApnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpCgojIGNsdXN0ZXJzIGRvbid0IGJyZWFrIHVwIGJ5IHRoZSBwcmVkaWN0ZWQgY2VsbCB0eXBlcwoKIyMjIyMjIyMjIyMjIGFub3RoZXIgcHJlZGljdGlvbnMgbm93IHVzaW5nIHRoZSBBSVcgb3JnYW5vaWRzCgpJZGVudHMoQUlXTUJPKSA8LSAicmVzMDhuYW1lcyIKRGVmYXVsdEFzc2F5KEFJV01CTykgPC0gIlJOQSIKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBBSVdNQk8gLHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gQUlXTUJPJHJlczA4bmFtZXMpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJE1CT0FJVy5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnTUJPQUlXLnByZWQnLCBsYWJlbCA9IFRSVUUpCiAKIyMgY2hlY2sgdGhlIHByb3BvcnRpb24gb2YgY2VsbCB0eXBlcyBwcmVkaWN0ZWQgaW4gZWFjaCBjbHVzdGVyCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKQpwci50LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyB0aGUgcHJlZGljdGVkIGNlbGwgdHlwZXMgbWFrZSBtb3JlIHNlbnNlIGZyb20gdGhlIEFJVzAwMiBvcmdhbm9pZAojIG5vdyBwcmVkaWN0IHdpdGggdGhlIEFJVzAwMiA2MCBkYXlzIG9yZ2Fub2lkCgpJZGVudHMoQUlXNjApIDwtICJjbHVzdGVyLmlkcyIKRGVmYXVsdEFzc2F5KEFJVzYwKSA8LSAiUk5BIgoKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IEFJVzYwLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IEFJVzYwJGNsdXN0ZXIuaWRzKSAKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkQUlXNjAucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0FJVzYwLnByZWQnLCBsYWJlbCA9IFRSVUUpCiAKIyMgY2hlY2sgdGhlIHByb3BvcnRpb24gb2YgY2VsbCB0eXBlcyBwcmVkaWN0ZWQgaW4gZWFjaCBjbHVzdGVyCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKQpwci50LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCiMgdHJ5IGJhciBjaGFydApnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpCgojIHNhdmUgb2piZWN0IHdpdGggcHJlZGljaXRvbnMKc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9OZXVyb25zMlByZWRpY3Rpb25zU2V1MzAwOTIwMjIuUkRTIikKCgoKYGBgCgoKTG9vayBhdCB0aGUgY2VsbCB0eXBlIHByZWRpY3Rpb25zIGluIGVhY2ggY2x1c3RlciAtIE5ldXJvbnMgMQoKYGBge3J9CiMgQUlXMDAyIDE2MCBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC42LCBzZXUucSRNQk9BSVcucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMSwgRnJlcSkpCgojIEFJVzAwMiAxNjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuNiwgc2V1LnEkQUlXNjAucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCA8LWFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDEsIEZyZXEpKQoKIyBBU1QyMyA2NSBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC42LCBzZXUucSRNQk9BU1QyMy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDEsIEZyZXEpKQoKCnByZWQudGFibGUgPC0gbWVyZ2UodG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwLHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwLCBieSA9ICdWYXIxJykKcHJlZC50YWJsZSA8LSBtZXJnZShwcmVkLnRhYmxlLCB0b3AucHJlZC5jZWxsdHlwZS5BU1QyMywgYnkgPSAnVmFyMScpCnByZWQudGFibGUKCmBgYAoKQmFzZWQgb24gdGhlIDMgZGlmZmVyZW50IHByZWRpY3Rpb25zIEkgY2FuIGxhYmxlIHRoZSBjZWxsIHR5cGVzCgowIC0gTlBDIG9yIGVhcmx5IG5ldXJvbnMKMSAtIGltbWF0dXJlIGV4Y2l0YXRvcnkgbmV1cm9ucwoyIC0gTlBDIG9yIGVhcmx5IG5ldXJvbnMKMyAtIFJHIG9yIE9saWdvcwo0LSBEb3BhbWluZXJnaWMgbmV1cm9ucyAtIHBvc3NpYmx5IGVhcmx5CjUgLSBOUEMgb3IgZWFybHkgbmV1cm9ucwo2IC0gUmFkaWFsIEdMaWEKClByZWRpY3Rpb25zIHdpdGggdGhlIERBIHN1YnR5cGVzIGZyb20gCgpgYGB7cn0KIyByZWFkIGluIHRoZSAKc2V1LnEgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL05ldXJvbjFMYWJsZWRTZXUzMDA5MjAyMi5SRFMiKQoKRGltUGxvdChzZXUucSkKCmBgYAoKVXNlIHRoZSBCaGFkdXJpIGRhdGFzZXQKCmBgYHtyfQoKIyByZWFkIGluIHRoZSByZWZlcmVuY2UgZGF0YXNldAojIGZyb20gQmhhZHVyaSBtaWRicmFpbiBhbmQgc3RyaWF0dW0Kc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9taWRicmFpbl9zdHJpYXR1bS5SRFMiKQp0YWJsZShzZXUuciRjZWxsX3R5cGUpCnRhYmxlKHNldS5yJGNlbGxfY2xhc3MpCnRhYmxlKHNldS5yJGNlbGxfY2x1c3RlcikKCklkZW50cyhzZXUucikgPC0gImNlbGxfY2x1c3RlciIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfY2x1c3RlcikKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5taWQuc3RyaS5wcmVkIDwtIElkZW50cyhzZXUucSkKcHJpbnQodGFibGUoc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEubWlkLnN0cmkucHJlZCcpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycpCgojIGRvIHRoZSBwcmVkaWN0aW9ucyBkaWZmZXIgd2l0aCB0aGUgbWFpbiBjZWxsIHR5cGUgZ3JvdXBzIGluc3RlYWQgb2YgdGhlIGNsdXN0ZXIgaW4gdGhlIHJlZmVyZW5jZSBkYXRhPyAKSWRlbnRzKHNldS5yKSA8LSAiY2VsbF90eXBlIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkY2VsbF90eXBlKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAncHJlZGljdGVkLmlkJykKCiMgZ29vZCBsYXJnZXN0IHByZWRpY3Rpb24gaXMgbmV1cm9ucy4gCgoKCmBgYAoKCgoKCkkgd2lsbCBhbHNvIGZpbmQgbWFya2VycyBhbmQgbG9vayBhdCBhIGxpc3Qgb2YgbmV1cm9uYWwgbWFya2VycwoKYGBge3J9CgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjYnCkNsdXN0ZXJNYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKHNldS5xLCBvbmx5LnBvcyA9IFRSVUUpCgp0b3A1IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBncm91cF9ieShjbHVzdGVyKSAlPiUgdG9wX24obj01LCB3dCA9IGF2Z19sb2cyRkMpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSB0b3A1JGdlbmUsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKCndyaXRlLmNzdihDbHVzdGVyTWFya2VycywiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9OZXVyb25zMUNsdXN0ZXJNYXJrZXJzNy5jc3YiKQoKCmBgYAoKRXhwbG9yZSBzb21lIEdlbmUgZXhwcmVzc2lvbiBsZXZlbHMKCmBgYHtyfQpmZWF0dXJlX2xpc3QgPSBjKCJNS0k2NyIsIlNPWDIiLCJQT1U1RjEiLCJETFgyIiwiUEFYNiIsIlNPWDkiLCJIRVMxIiwiTkVTIiwiUkJGT1gzIiwiTUFQMiIsIk5DQU0xIiwiQ0QyNCIsIkdSSUEyIiwiR1JJTjJCIiwiR0FCQlIxIiwiR0FEMSIsIkdBRDIiLCJHQUJSQTEiLCJHQUJSQjIiLCJUSCIsIkFMREgxQTEiLCJMTVgxQiIsIk5SNEEyIiwiQ09SSU4iLCJDQUxCMSIsIktDTko2IiwiQ1hDUjQiLCJJVEdBNiIsIlNMQzFBMyIsIkNENDQiLCJBUVA0IiwiUzEwMEIiLCAiUERHRlJBIiwiT0xJRzIiLCJNQlAiLCJDTEROMTEiLCJWSU0iLCJWQ0FNMSIpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0KSArUm90YXRlZEF4aXMoKQoKUERfcG91bGluID0gYygiVEgiLCJTTEM2QTMiLCJTTEMxOEEyIiwiU09YNiIsIk5ETkYiLCJTTkNHIiwiQUxESDFBMSIsIkNBTEIxIiwiVEFDUjIiLCJTTEMxN0E2IiwiU0xDMzJBMSIsIk9UWDIiLCJHUlAiLCJMUEwiLCJDQ0siLCJWSVAiKQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IFBEX3BvdWxpbiwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IFBEX3BvdWxpbikrUm90YXRlZEF4aXMoKQoKZWFscnlOZXVyID0gYygiRENYIiwiTkVVUk9EMSIsIlRCUjEiKQpwcm9saWZlcmF0aW9uID0gYygiUENOQSIsIk1LSTY3IikKbmV1cmFsc3RlbSA9IGMoIlNPWDIiLCJORVMiLCJQQVg2IiwiTUFTSDEiKQoKZmVhdHVyZV9saXN0IDwtIGMoIkRDWCIsIk5FVVJPRDEiLCJUQlIxIiwiUENOQSIsIk1LSTY3IiwiU09YMiIsIk5FUyIsIlBBWDYiLCJNQVNIMSIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QpK1JvdGF0ZWRBeGlzKCkKIyBubyBwcm9saWZlcmF0aW9uIG1hcmtlciBleHByZXNzaW9uICBQQ05BIG9yIE1LSTY3CiMgY2x1c3RlciA0IERBIG5ldXJvbnMgLSBzaG93cyBlYXJseSBuZXVyb24gbWFya2VyIGFuZCBsb3cgUEFYIDQKIyBjbHVzdGVyIDMgaGFzIGhpZ2hlciBTT1gyIC0gbmV1cm9ibGFzdCBtYXJrZXIgLyBOUEMgbWFya2VyCgptYXRfbmV1cm9uID0gYygiUkJGT1gzIiwiU1lQIiwiRExHNDUiLCJWQU1QMSIsIlZBTVAyIiwiVFVCQjMiLCJTWVQxIiwiQlNOIiwiSE9NRVIxIiwiU0xDMTdBNiIpIAojIE5ldU4gaXMgRk9YMyAtIFJCRk9YMwojIFBTRDk1IGFsc28gU1AtOTAgb3IgRExHNAojIFZHTFVUMiBpcyBTTEMxN0E2CkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBtYXRfbmV1cm9uLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCiMgY2x1c3RlciA0IGFsc28gc2hvdyBtYXR1cmUgbmV1cm9uIG1hcmtlcnMKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBtYXRfbmV1cm9uKStSb3RhdGVkQXhpcygpCiMgZXhjaXRhdG9yeSBuZXVyb24gbWFya2VycwpleCA9IGMoIkdSSUEyIiwiR1JJQTEiLCJHUklBNCIsIkdSSU4xIiwiR1JJTjJCIiwiR1JJTjJBIiwiR1JJTjNBIiwiR1JJTjMiLCJHUklQMSIsIkNBTUsyQSIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBleCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGV4KStSb3RhdGVkQXhpcygpCiMgaW5oaWJpdG9yeSBuZXVyb24gbWFya2VycwppbmggPSBjKCJHQUQxIiwiR0FEMiIsICJHQVQxIiwiUFZBTEIiLCJHQUJSMiIsIkdBQlIxIiwiR0JSUjEiLCJHQUJSQjIiLCJHQUJSQjEiLCJHQUJSQjMiLCJHQUJSQTYiLCJHQUJSQTEiLCJHQUJSQTQiLCJUUkFLMiIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBpbmgsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBpbmgpK1JvdGF0ZWRBeGlzKCkKIyBjbHVzdGVyIDQgaXMgbW9yZSBleGNpdGF0b3J5IHRoYW4gaW5oYml0b3J5IGJ1dCBuZWl0aGVyIG1hcmtlciBzZXQgaGFzIG11Y2ggZXhwcmVzc2lvbiAKCgoKYGBgCgoKQ2hlY2tvdXQgdGhlIEVucmljaGVyIGNlbGwgdHlwZSBsaWJyYXJpZXMgZnJvbSAKCmBgYHtyfQojIHRlc3QgbWFya2VycyBmb3IgdGhlIDcgY2x1c3RlcnMgaW4gTmV1cm9uczEgCgpsaWJyYXJ5KGRldnRvb2xzKQppbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKbGlicmFyeShlbnJpY2hSKQoKCnNldEVucmljaHJTaXRlKCJFbnJpY2hyIikgIyBIdW1hbiBnZW5lcwojIGxpc3Qgb2YgYWxsIHRoZSBkYXRhYmFzZXMKCmRicyA8LSBsaXN0RW5yaWNockRicygpCmRicwojIGxpYmFyaWVzIHdpdGggY2VsbCB0eXBlcwoKZGIgPC0gYygnQWxsZW5fQnJhaW5fQXRsYXNfdXAnLCdEZXNjYXJ0ZXNfQ2VsbF9UeXBlc19hbmRfVGlzc3VlXzIwMjEnLAogICAgICAgICdDZWxsTWFya2VyX0F1Z21lbnRlZF8yMDIxJywnQXppbXV0aF9DZWxsX1R5cGVzXzIwMjEnKQoKIyBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBOVUxMKQoKTjEuYzAgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDAgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzAkZ2VuZQoKTjEuYzAuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzAuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzAuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzAuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmMwLkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzAuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jMC5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgojIGNsdXN0ZXIgMCBjb3VsZCBiZSBoeXBvdGhhbG11cywgREEgbmV1cm9ucyBBMTMKCk4xLmMxIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAxICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMxJGdlbmUKCk4xLmMxLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jMS5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmMxLkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzEuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jMS5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgojIGNsdXN0ZXIgMTsgb2xmYWN0b3J5IGJ1bGIsIG5ldXJhbCBwbGF0ZSwgbWF5YmUgUmFkaWFsIEdsaWEsIApOMS5jMiA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMiAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jMiRnZW5lCgpOMS5jMi5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jMi5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMi5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMi5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMi5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzIuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jMi5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmMyLkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzIuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDIgc29tZSBicmFpbiBudWNsZXVzLCBuZXVyYWwgc3RlbQoKTjEuYzMgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDMgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzMkZ2VuZQoKTjEuYzMuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzMuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzMuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzMuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzMuRXJbWzRdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmMzLkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzMuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jMy5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgpOMS5Fci5nZW5lcy40IDwtIE4xLmMzLkVyW1s0XV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjQKCiMgY2x1c3RlciAzIHN0cm9tYWwgY2VsbCBvZiB0aHltdXMsIGVtYnJ5b25pYyBhc3Ryb2N5dGVzLCBPUEMsIE5LIGNlbGxzLCBtb25vY3l0ZXMKCk4xLmM0IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA0ICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmM0JGdlbmUKCk4xLmM0LkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM0LkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jNC5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmM0LkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzQuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jNC5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgojIERlbnRhdGUgZ3lydXMgLSBkaWZmZXJlbnQgY29ydGljYWwgbGF5ZXJzLCBuZXVyb25zLCBuZXVyb25zLCBOUEMsIG5ldXJvbnMgR0FCQSxHTFVUCgpOMS5jNSA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gNSAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jNSRnZW5lCgpOMS5jNS5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jNS5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNS5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNS5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNS5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzUuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jNS5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmM1LkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzUuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDUgaGlwcG9jYW1wdXMsIGVuZG90aGVsaWFsIGNlbGxzLCBwZXJpY3l0ZXMKCk4xLmM2IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA2ICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmM2JGdlbmUKCk4xLmM2LkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jNi5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmM2LkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzYuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jNi5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgojIGNsdXN0ZXIgNiBicmFpbiBjb3J0ZXgsIHNod2FubiBjZWxsLCBlbmRvdGhlbGlhbCwgcGVyaWN5dGUsIEdBQkEKCgpgYGAKCgpBZnRlciBwcmVkaWN0aW5nIHdpdGggdGhlIGJyYWluIGRhdGEgSSB0aGluayB1c2luZyBhIGhpZ2hlciBjbHVzdGVyIG51bWJlciB3aWxsIHdvcmsgYmV0dGVyCgoKYGBge3J9CgojIHJlczEuMiBoYXMgMTAgY2x1c3RlcnMKCgojIEFJVzAwMiAxMjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjEuMiwgc2V1LnEkTUJPQUlXLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3MTIwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXcxMjApIDwtIE5VTEwKZGYudG9wLmFpdzEyMCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKQoKIyBBSVcwMDIgNjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjEuMiwgc2V1LnEkQUlXNjAucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCA8LWFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3NjAgPC0gdG9wLnByZWQuY2VsbHR5cGUuQUlXNjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFpdzYwKSA8LSBOVUxMCiMgc29tZXRoaW5nIHdlbnQgd3JvbmcgYW5kIHRoZXJlIGFyZSB0b28gbWFueSBjbHVzdGVyIDAgcHJlZGljdGlvbnMKZGYudG9wLmFpdzYwIDwtIGRmLnRvcC5haXc2MCAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyLCAzLCA0KSkKZGYudG9wLmFpdzYwJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkKCiMgQVNUMjMgMTY1IGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4xLjIsIHNldS5xJE1CT0FTVDIzLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5BU1QyMyA8LSB0b3AucHJlZC5jZWxsdHlwZS5BU1QyM1tvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQVNUMjMpIDwtIE5VTEwKZGYudG9wLkFTVDIzJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykKZGYudG9wLkFTVDIzIDwtIGRmLnRvcC5haXc2MCAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyMikpCgojIyMgYWRkIGluIHRoZSBwcmVkaWN0aW9uIGZyb20gYnJhaW4gZGF0YSBCaGFkdXJpIG1pZGJyYWluIGFuZCBzdHJpYXR1bQp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjEuMiwgc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQmhhIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuQmhhIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkJoYVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5CaGEkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQmhhJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5CaGEpIDwtIE5VTEwKZGYudG9wLkJoYSRJIDwtIHJvdy5uYW1lcyhkZi50b3AuQmhhKQoKCnByZWQudGFibGUgPC0gbWVyZ2UoZGYudG9wLkFTVDIzLCBkZi50b3AuYWl3NjAsIGJ5ID0gJ0knLCBhbGwgPSBUUlVFKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5haXcxMjAsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlIDwtIG1lcmdlKHByZWQudGFibGUsIGRmLnRvcC5CaGEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlCgojIGNsdXN0ZXIgMyBpcyBwcmVkaWN0ZWQgYXMgT2xpZ28sIFJHLCBhc3RybwoKYGBgCgoKCgpMaWJyYXJ5IG9mIHRpc3N1ZSBjZWxsIHR5cGVzIGZvciB1cCByZWd1bGF0ZWQgZ2VuZXMgcGVyIGNsdXN0ZXIKMCAtIGh5cG90aGFsbXVzLCBEQSBBMTMKMS0gbmV1cmFsIHBsYXRlLCBSYWRpYWwgR2xpYQoyIC0gTmV1cmFsIHN0ZW0KMyAtIHN0cm9tYWwsIGFzdHJvIE9QQwo0IC0gTmV1cm9ucwo1IC0gZW5kb3RoZWxpYWwsIHBlcmljeXRlCjYgLSBtYXliZSBuZXVyb25zIG1heWJlIG5vdAoKCgoKCgpCeSB0aGUgY29tYmluZWQgaW5mb3JtYXRpb24gLSBhbm5vdGF0ZSB0aGUgY2x1c3RlcnMgaW4gTmV1cm9uczEKCmBgYHtyfQojQmFzZWQgb24gdGhlIDMgZGlmZmVyZW50IHByZWRpY3Rpb25zIEkgY2FuIGxhYmxlIHRoZSBjZWxsIHR5cGVzCgojMCAtIE5QQyBvciBlYXJseSBuZXVyb25zCiMxIC0gaW1tYXR1cmUgZXhjaXRhdG9yeSBuZXVyb25zCiMyIC0gTlBDIG9yIGVhcmx5IG5ldXJvbnMKIzMgLSBSRyBvciBPbGlnb3MKIzQtIERvcGFtaW5lcmdpYyBuZXVyb25zIC0gcG9zc2libHkgZWFybHkKIzUgLSBOUEMgb3IgZWFybHkgbmV1cm9ucwojNiAtIFJhZGlhbCBHbGlhCgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjYnCmNsdXN0ZXIuaWRzIDwtIGMoIkltbWF0dXJlTmV1cm9ucyIsIk5ldXJvbnMiLCJOUEMiLCJPUEMtUkciLCJEQW5ldXJvbnMiLAogICAgICAgICAgICAgICAgICJPdGhlciIsIlJHIikKdW5pcXVlKHNldS5xJFJOQV9zbm5fcmVzLjAuNikKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1LnEpCnNldS5xIDwtIFJlbmFtZUlkZW50cyhzZXUucSwgY2x1c3Rlci5pZHMpCnNldS5xJHN1Ymdyb3VwcyA8LSBJZGVudHMoc2V1LnEpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnc3ViZ3JvdXBzJywgcmVwZWwgPSBUUlVFKQoKCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvTmV1cm9uMUxhYmxlZFNldTMwMDkyMDIyLlJEUyIpCgoKCmBgYAoKYGBge3J9CmxpYnJhcnkoY2x1c3RyZWUpCmNsdXN0cmVlKHNldS5xKQoKYGBgCgoKCgpBZnRlciBwcmVkaWN0aW5nIHdpdGggdGhlIGJyYWluIGRhdGEgQmhhZHVyaSBNaWRicmFpbiBhbmQgU3RyaWF0dW0gLSBjaG9vc2UgbWFpbiBjZWxsIHR5cGVzIHRvIGxhYmVsClRyaWVkIHRoZSBwcmVkaWN0aW9ucyB3aXRoIG1vcmUgY2x1c3RlcnMgMC05IHJlcyAxLjIKCmBgYHtyfQoKCiMwIC0gTlBDIG9yIGVhcmx5IG5ldXJvbnMgICAgICAgICAgPSAwLDcsMgojMSAtIGltbWF0dXJlIGV4Y2l0YXRvcnkgbmV1cm9ucyA9IDEsOAojMiAtIE5QQyBvciBlYXJseSBuZXVyb25zICAgICAgICAgPSA0LDYKIzMgLSBSRyBvciBPbGlnb3MgICAgICAgICAgICAgICAgIDksMiw3CiM0LSBEb3BhbWluZXJnaWMgbmV1cm9ucyAtIHBvc3NpYmx5IGVhcmx5ICAgPSAzCiM1IC0gTlBDIG9yIGVhcmx5IG5ldXJvbnMgICA9IDUKIzYgLSBSYWRpYWwgR2xpYSAgID0gOSAKCgojIDAgLSBuZXVyb25zIChOUEMpCiMgMSAtIG5ldXJvbnMgKE5QQykKIyAyIC0gYXN0cm8gKE5QQykKIyAzIC0gbmV1cm9ucyAoREEpCiMgNCAtIG5ldXJvbnMgKE5QQykKIyA1IC0gRW5kb3RoZWxpYWwgKHByZWRpY3RlZCBmcm9tIEJoYSwgcHJlZGljdGVkIGFzIE5QQykKIyA2IC0gTmV1cm9ucy8gR2xpYQojIDcgLSBBc3Ryb2N5dGVzIFJHCiMgOCAtIE5ldXJvbnMKIyA5IC0gRW5kb3RoZWxpYWwgUkcsIG5ldXJvbnMKCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjEuMicKY2x1c3Rlci5pZHMgPC0gYygiTmV1cm9ucyIsIk5ldXJvbnMiLCJOZXVyb25zIiwiTmV1cm9ucyIsIk5ldXJvbnMiLAogICAgICAgICAgICAgICAgICJFbmRvdGhlbGlhbCIsIk5ldXJvbnMiLCJHbGlhIiwiTmV1cm9ucyIsIlJhZGlhbCBHbGlhIikKdW5pcXVlKHNldS5xJFJOQV9zbm5fcmVzLjAuNikKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1LnEpCnNldS5xIDwtIFJlbmFtZUlkZW50cyhzZXUucSwgY2x1c3Rlci5pZHMpCnNldS5xJENlbGxfVHlwZXMgPC0gSWRlbnRzKHNldS5xKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ0NlbGxfVHlwZXMnLCByZXBlbCA9IFRSVUUpCgpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL05ldXJvbjFMYWJsZWRTZXUzMDA5MjAyMi5SRFMiKQoKCmBgYAoKCgoKCgoKCiMjIyBOZXh0IFJlcGVhdCBldmVyeXRoaW5nIGZvciBOZXVyb25zMgoKYGBge3J9CiMgZXhwbG9yZSBmaWx0ZXJpbmcKc2V1IDwtIE5ldXJvbnMyCnNldQpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBuY29sID0gMykKClZsblBsb3Qoc2V1LCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiKSwgeS5tYXggPSAyMDAwKQpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiksIHkubWF4ID0gMzUwKQpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkNvdW50X1JOQSIpLCB5Lm1heCA9IDIwMDApCgojIGZpbHRlciBtb3JlIGNlbGxzCgpzZXUuZnQgPC0gc3Vic2V0KHNldSwgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gMzAwICYgbkNvdW50X1JOQSA+IDUwMCAmIG5Db3VudF9STkEgPCAxMDAwMCkgCnNldS5mdAoKIyAxNzYwNCBzYW1wbGVzIHdpdGggMjUwIG5GZWF0dXJlX1JOQQojIDk2NTcgd2l0aCAgbkZlYXR1cmUgMzAwIGFuZCBuQ091bnQgNTAwCgoKYGBgCgoKRG91YmxldCBmaW5kZXIgCgpgYGB7cn0Kc3VwcHJlc3NNZXNzYWdlcyhyZXF1aXJlKERvdWJsZXRGaW5kZXIpKQoKIyBmaWx0ZXJpbmcgb3V0IE1BTEFUMSBhbmQgbWl0b2Nob25kcmlhbCBnZW5lcwoKc2V1LmZ0IDwtIHNldS5mdFshZ3JlcGwoIk1BTEFUMSIsIHJvd25hbWVzKHNldSkpLCBdCnNldS5mdCA8LSBzZXUuZnRbIWdyZXBsKCJeTVQtIiwgcm93bmFtZXMoc2V1LmZ0KSksIF0KCiMgbGlrZSBpbiB0aGUgdHV0b3JpYWwgSSdtIGZvbGxvd2luZyBNQUxBVDEgaXMgdGhlIHRvcCBtb3N0IGV4cHJlc3NlZCBnZW5lLiAgVGhlIHRvcCBnZW5lcyBhcmUgYSBsb3Qgb2YgTVQgYW5kIFJpYm9zb21hbCBnZW5lcwoKc2V1LmZ0W1sicGVyY2VudC5yYiJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChzZXUuZnQsIHBhdHRlcm4gPSAiXlJQIikKCnNldS5kID0gTm9ybWFsaXplRGF0YShzZXUuZnQpCnNldS5kID0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1LmQsIHZlcmJvc2UgPSBGKQpzZXUuZCA9IFNjYWxlRGF0YShzZXUuZCwgdmFycy50by5yZWdyZXNzID0gYygibkZlYXR1cmVfUk5BIiwgInBlcmNlbnQubXQiKSwKICAgIHZlcmJvc2UgPSBGKQpzZXUuZCA9IFJ1blBDQShzZXUuZCwgdmVyYm9zZSA9IEYsIG5wY3MgPSAzMCkKc2V1LmQgPSBSdW5VTUFQKHNldS5kLCBkaW1zID0gMToxMCwgdmVyYm9zZSA9IEYpCgpuRXhwIDwtIHJvdW5kKG5jb2woc2V1LmQpICogMC4wOCkgICMgZXhwZWN0IG1vcmUgZG91YmxldHMgYmVjYXVzZSB0aGVyZSBpcyBhIGxvdCBtb3JlIGNlbGxzCnNldS5kIDwtIGRvdWJsZXRGaW5kZXJfdjMoc2V1LmQsIHBOID0gMC4yNSwgcEsgPSAwLjA5LCBuRXhwID0gbkV4cCwgUENzID0gMToxMCkKCgojIG5hbWUgb2YgdGhlIERGIHByZWRpY3Rpb24gY2FuIGNoYW5nZSwgc28gZXh0cmFjdCB0aGUgY29ycmVjdCBjb2x1bW4gbmFtZS4KREYubmFtZSA9IGNvbG5hbWVzKHNldS5kQG1ldGEuZGF0YSlbZ3JlcGwoIkRGLmNsYXNzaWZpY2F0aW9uIiwgY29sbmFtZXMoc2V1LmRAbWV0YS5kYXRhKSldCgoKY293cGxvdDo6cGxvdF9ncmlkKG5jb2wgPSAyLCBEaW1QbG90KHNldS5kLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikgKyBOb0F4ZXMoKSwKICAgIERpbVBsb3Qoc2V1LmQsIGdyb3VwLmJ5ID0gREYubmFtZSkgKyBOb0F4ZXMoKSkKClZsblBsb3Qoc2V1LmQsIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIGdyb3VwLmJ5ID0gREYubmFtZSwgcHQuc2l6ZSA9IDAuMSkKCmBgYAoKClJlbW92ZSB0aGUgZG91YmxldCBjZWxscwoKYGBge3J9CgpzZXUuZCA8LSBzZXUuZFssIHNldS5kQG1ldGEuZGF0YVssIERGLm5hbWVdPT0gIlNpbmdsZXQiXQpkaW0oc2V1LmQpCmRpbShzZXUpCiMgOTY1NyBjZWxscyBwcmUgZmlsdGVyCiMgODg4NCBjZWxscyBhZnRlciBmaWx0ZXJpbmcKIyBub3RlIHRoZSBwZXJjZW50IGRvdWJsZXMgZXhwZWN0ZWQgaXMgY2xvc2UgdG8gdGhlIHBlcmNlbnQgZGV0ZWN0ZWQKCmBgYAoKClJlcGVhdCB3b3JrZmxvdyB3aXRoIGRvdWJsZXQgcmVtb3ZlZCBkYXRhIGFuZCBmaW5kIGNsdXN0ZXJzIGZvciAKCmBgYHtyfQoKCnNldSA8LSBOb3JtYWxpemVEYXRhKHNldS5kLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKc2V1IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldSwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQpzZXUgPC0gU2NhbGVEYXRhKHNldSkKc2V1IDwtIFJ1blBDQShzZXUpCnNldSA8LSBSdW5VTUFQKHNldSwgcmVkdWN0aW9uID0gInBjYSIsIG4ubmVpZ2hib3JzID0gNDMsIGRpbXMgPSAxOjMwKQpEaW1QbG90KHNldSwgcmVkdWN0aW9uID0gInVtYXAiKQoKc2V1LnEgPC0gRmluZE5laWdoYm9ycyhzZXUsIGRpbXMgPSAxOjI1LCBrLnBhcmFtID0gNDMpCnNldS5xIDwtIEZpbmRDbHVzdGVycyhzZXUucSwgcmVzb2x1dGlvbiA9IGMoMCwwLjIsMC40LDAuNikpCgpsaWJyYXJ5KGNsdXN0cmVlKQpjbHVzdHJlZShzZXUucSkKCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4yJykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjQnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMS4yJykKCmBgYAoKTGFiZWwgY2VsbCB0eXBlcyB1c2luZyB0aGUgbGFiZWwgdHJhbnNmZXIKCmBgYHtyfQoKCgojIFNOQ0EgYW5kIGNvbnRyb2wgbWlkYnJhaW4gb3JnYW5vaWRzIDE2NSBkYXlzIGluIGN1bHR1cmUKTUJPIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FTVDIzX0JyYWluQ29tbS9NQk9jbHVzdGVyc19uYW1lczI5MDcyMDIxLnJkcyIpCgojIE1pZGJyYWluICBBSVcwMDIgMTIwIGRheXMgaW4gY3VsdHVyZQpBSVdNQk8gPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQUlXdHJpbzEyMGRheXMvTU9pbnRlZ3JhdGVkQ2x1c3RlcksxMjNyZXMwLjgubmFtZXNfbm92MTZfMjAyMSIpCgojIE1pZGJyYWluIEFJVzAwMiA2MCBkYXlzIGluIGN1bHR1cmUKCkFJVzYwIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL0FJV3RyaW82MGRheXMvQVdJMDAyUGFya2luS09QaW5rS082MGRheXNfbGFiZWxzXzE0MDUyMDIyLnJkcyIpCgoKIyBxdWVyeQojc2V1LnEgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL05ldXJvbnNGaWx0ZXJlZFNldTI4MDkyMDIyLlJEUyIpCgoKI2ZpcnN0IHByZWRpY3Qgd2l0aCB0aGUgTUJPIGRhdGEKSWRlbnRzKE1CTykgPC0gImNsdXN0ZXJfbGFiZWxzIgpEZWZhdWx0QXNzYXkoTUJPKSA8LSAiUk5BIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwpwcmludCgiZmluZGluZyByZWZlcmVuY2UgYW5jaG9ycyIpCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBNQk8gLHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gTUJPJGNsdXN0ZXJfbGFiZWxzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRNQk9BU1QyMy5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnTUJPQVNUMjMucHJlZCcsIGxhYmVsID0gVFJVRSkKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJHByZWRpY3RlZC5pZCkpCnByLnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUocHJvcC50YWJsZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJHByZWRpY3RlZC5pZCkpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQoKCiMgdHJ5IGJhciBjaGFydApnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpCgojIGNsdXN0ZXJzIGRvbid0IGJyZWFrIHVwIGJ5IHRoZSBwcmVkaWN0ZWQgY2VsbCB0eXBlcwoKIyMjIyMjIyMjIyMjIGFub3RoZXIgcHJlZGljdGlvbnMgbm93IHVzaW5nIHRoZSBBSVcgb3JnYW5vaWRzCgpJZGVudHMoQUlXTUJPKSA8LSAicmVzMDhuYW1lcyIKRGVmYXVsdEFzc2F5KEFJV01CTykgPC0gIlJOQSIKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBBSVdNQk8gLHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gQUlXTUJPJHJlczA4bmFtZXMpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJE1CT0FJVy5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnTUJPQUlXLnByZWQnLCBsYWJlbCA9IFRSVUUpCiAKIyMgY2hlY2sgdGhlIHByb3BvcnRpb24gb2YgY2VsbCB0eXBlcyBwcmVkaWN0ZWQgaW4gZWFjaCBjbHVzdGVyCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKQpwci50LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyB0aGUgcHJlZGljdGVkIGNlbGwgdHlwZXMgbWFrZSBtb3JlIHNlbnNlIGZyb20gdGhlIEFJVzAwMiBvcmdhbm9pZAojIG5vdyBwcmVkaWN0IHdpdGggdGhlIEFJVzAwMiA2MCBkYXlzIG9yZ2Fub2lkCgpJZGVudHMoQUlXNjApIDwtICJjbHVzdGVyLmlkcyIKRGVmYXVsdEFzc2F5KEFJVzYwKSA8LSAiUk5BIgoKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IEFJVzYwLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IEFJVzYwJGNsdXN0ZXIuaWRzKSAKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkQUlXNjAucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0FJVzYwLnByZWQnLCBsYWJlbCA9IFRSVUUpCiAKIyMgY2hlY2sgdGhlIHByb3BvcnRpb24gb2YgY2VsbCB0eXBlcyBwcmVkaWN0ZWQgaW4gZWFjaCBjbHVzdGVyCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKQpwci50LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHByb3AudGFibGUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRwcmVkaWN0ZWQuaWQpKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBzYXZlIG9qYmVjdCB3aXRoIHByZWRpY2l0b25zCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvTmV1cm9uczJQcmVkaWN0aW9uc1NldTMwMDkyMDIyLlJEUyIpCgoKCmBgYAoKClByZWRpY3QgZnJvbSBCcmFpbiBCaGFoYW5pCgpgYGB7cn0KCnNldS5xIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9OZXVyb25zMkxhYmVsc1NldTMwMDkyMDIyLlJEUyIpCgoKIyByZWFkIGluIHRoZSByZWZlcmVuY2UgZGF0YXNldAojIGZyb20gQmhhZHVyaSBtaWRicmFpbiBhbmQgc3RyaWF0dW0Kc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9taWRicmFpbl9zdHJpYXR1bS5SRFMiKQp0YWJsZShzZXUuciRjZWxsX3R5cGUpCnRhYmxlKHNldS5yJGNlbGxfY2xhc3MpCnRhYmxlKHNldS5yJGNlbGxfY2x1c3RlcikKCklkZW50cyhzZXUucikgPC0gImNlbGxfY2x1c3RlciIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfY2x1c3RlcikKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5taWQuc3RyaS5wcmVkIDwtIElkZW50cyhzZXUucSkKcHJpbnQodGFibGUoc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEubWlkLnN0cmkucHJlZCcpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycpCgojIGRvIHRoZSBwcmVkaWN0aW9ucyBkaWZmZXIgd2l0aCB0aGUgbWFpbiBjZWxsIHR5cGUgZ3JvdXBzIGluc3RlYWQgb2YgdGhlIGNsdXN0ZXIgaW4gdGhlIHJlZmVyZW5jZSBkYXRhPyAKSWRlbnRzKHNldS5yKSA8LSAiY2VsbF90eXBlIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkY2VsbF90eXBlKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAncHJlZGljdGVkLmlkJykKCiMgZ29vZCBsYXJnZXN0IHByZWRpY3Rpb24gaXMgbmV1cm9ucy4gCgoKYGBgCgoKClNlZSB0aGUgdG9wIHByZWRpY3Rpb25zIGZvciBlYWNoIGNsdXN0ZXIgaW4gTmV1cm9uczIgcmVzIDA2IAoKCmBgYHtyfQoKIyBBSVcwMDIgMTIwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjYsIHNldS5xJE1CT0FJVy5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzEyMCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKSA8LSBOVUxMCmRmLnRvcC5haXcxMjAgPC0gZGYudG9wLmFpdzEyMCAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyMCwgMjEsIDIyLCAyMywgMjQpKQpkZi50b3AuYWl3MTIwJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5haXcxMjApCgojIEFJVzAwMiA2MCBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC42LCBzZXUucSRBSVc2MC5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwIDwtYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5haXc2MCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVc2MFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BSVc2MCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3NjApIDwtIE5VTEwKIyBzb21ldGhpbmcgd2VudCB3cm9uZyBhbmQgdGhlcmUgYXJlIHRvbyBtYW55IGNsdXN0ZXIgMCBwcmVkaWN0aW9ucwojZGYudG9wLmFpdzYwIDwtIGRmLnRvcC5haXc2MCAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyLCAzLCA0KSkKZGYudG9wLmFpdzYwJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkKCiMgQVNUMjMgMTY1IGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjYsIHNldS5xJE1CT0FTVDIzLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5BU1QyMyA8LSB0b3AucHJlZC5jZWxsdHlwZS5BU1QyM1tvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQVNUMjMpIDwtIE5VTEwKZGYudG9wLkFTVDIzIDwtIGRmLnRvcC5BU1QyMyAlPiUgIGZpbHRlcighcm93X251bWJlcigpICVpbiUgYygyMCwgMjEsIDIyLCAyMywgMjQsIDI1KSkKZGYudG9wLkFTVDIzJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykKCiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC42LCBzZXUucSRCaGEubWlkLnN0cmkucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5CaGEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkJoYSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5CaGEkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkJoYSkgPC0gTlVMTApkZi50b3AuQmhhJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGEpCgoKcHJlZC50YWJsZSA8LSBtZXJnZShkZi50b3AuQVNUMjMsIGRmLnRvcC5haXc2MCwgYnkgPSAnSScsIGFsbCA9IFRSVUUpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLmFpdzEyMCwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYSwgYnkgPSAnSScpCnByZWQudGFibGUKCiMgY2x1c3RlciAzIGlzIHByZWRpY3RlZCBhcyBPbGlnbywgUkcsIGFzdHJvCgoKCgpgYGAKCgpgYGB7cn0KIyByZXMxLjIgaGFzIDE1IGNsdXN0ZXJzCgojIEFJVzAwMiAxMjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjEuMiwgc2V1LnEkTUJPQUlXLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3MTIwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXcxMjApIDwtIE5VTEwKZGYudG9wLmFpdzEyMCA8LSBkZi50b3AuYWl3MTIwICU+JSAgZmlsdGVyKCFyb3dfbnVtYmVyKCkgJWluJSBjKDI2LCAyNywgMjgsIDI5LCAzMCkpCmRmLnRvcC5haXcxMjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzEyMCkKCiMgQUlXMDAyIDYwIGRheXMgcHJlZGljdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4xLjIsIHNldS5xJEFJVzYwLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAgPC1hcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFpdzYwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkgPC0gTlVMTAojIHNvbWV0aGluZyB3ZW50IHdyb25nIGFuZCB0aGVyZSBhcmUgdG9vIG1hbnkgY2x1c3RlciAwIHByZWRpY3Rpb25zCiNkZi50b3AuYWl3NjAgPC0gZGYudG9wLmFpdzYwICU+JSAgZmlsdGVyKCFyb3dfbnVtYmVyKCkgJWluJSBjKDIsIDMsIDQpKQpkZi50b3AuYWl3NjAkSSA8LSByb3cubmFtZXMoZGYudG9wLmFpdzYwKQoKIyBBU1QyMyAxNjUgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjEuMiwgc2V1LnEkTUJPQVNUMjMucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkFTVDIzIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykgPC0gTlVMTApkZi50b3AuQVNUMjMgPC0gZGYudG9wLkFTVDIzICU+JSAgZmlsdGVyKCFyb3dfbnVtYmVyKCkgJWluJSBjKDI2LCAyNywgMjgsIDI5LCAzMCwgMzEpKQpkZi50b3AuQVNUMjMkSSA8LSByb3cubmFtZXMoZGYudG9wLkFTVDIzKQoKCiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMS4yLCBzZXUucSRCaGEubWlkLnN0cmkucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5CaGEgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5CaGEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkJoYSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5CaGEkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLkJoYSkgPC0gTlVMTApkZi50b3AuQmhhJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGEpCgoKcHJlZC50YWJsZSA8LSBtZXJnZShkZi50b3AuQVNUMjMsIGRmLnRvcC5haXc2MCwgYnkgPSAnSScsIGFsbCA9IFRSVUUpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLmFpdzEyMCwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYSwgYnkgPSAnSScpCnByZWQudGFibGUKCiMgbmV1cm9ucyA5OCBtYXRjaGVzIHVwIHdpdGggREEgbmV1cm9ucwoKYGBgCgoKCgpXaGF0IGNlbGwgdHlwZXMgYXJlIHByZWRpY3RlZCBhY3Jvc3MgdGhlIDMgcmVmZXJlbmNlcwoKMCAtIE5ldXJvbnMgZWFybHkgLCBOUEMsIG5ldXJvbnMgZXhjaXRhdG9yeQoxIC0gTmV1cm9ucyBlYXJseSwgTlBDCjIgLSBOZXVyb25zIGVhcmx5LCBOUEMsIG5ldXJvbnMgZXhjaXRhdG9yeSBzb21lIERBIG5ldXJvbnMKMyAtIE9saWdvLCBSRywgCjQgLSBFeGNpdGF0b3J5IG5ldXJvbnMsIE5QQywgZWFybHkgbmV1cm9ucwo1IC0gREEgbmV1cm9ucywgZWFybHkgREEgbmV1cm9ucwo2IC0gbmV1cm9ucyBpbW1hdHVyZSBOUEMKNyAtIERBIG5ldXJvbnMKOCAtIFJHLCBvbGlnbywgT1BDLCBOUEMKOSAtIFJhZGlhbCBHbGlhCjEwIC0gTlBDLCBuZXVyb25zLCBvbGlnbwoxMSAtIE5QQywgbmV1cm9ucywgb2xpZ28KCgpGaW5kIGNsdXN0ZXIgbWFya2VycyBhbmQgc2VlIGhvdyB0aG9zZSB3b3VsZCBhbm5vdGF0ZQoKYGBge3J9CklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuNicKQ2x1c3Rlck1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc2V1LnEsIG9ubHkucG9zID0gVFJVRSkKCnRvcDUgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuPTUsIHd0ID0gYXZnX2xvZzJGQykKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IHRvcDUkZ2VuZSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQoKd3JpdGUuY3N2KENsdXN0ZXJNYXJrZXJzLCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL05ldXJvbnMxQ2x1c3Rlck1hcmtlcnMxMS5jc3YiKQoKYGBgCkNsdXN0ZXIgMCBoYXMgZmV3ZXIgbWFya2Vycy4gCjIgYW5kIDUgaGF2ZSBzaW1pbGFyIHVwIHJlZyBtYXJrZXJzCjMgYW5kIDQgYWxzbyBvdmVybGFwCgoKCkxvb2sgYXQgdGhlIGNsdXN0ZXIgbWFya2VycyBpbiBjZWxsIHR5cGUgbGlicmFyaWVzIGZvciBOZXVyb25zIDIKCmBgYHtyfQpsaWJyYXJ5KGVucmljaFIpCmRiIDwtIGMoJ0FsbGVuX0JyYWluX0F0bGFzX3VwJywnRGVzY2FydGVzX0NlbGxfVHlwZXNfYW5kX1Rpc3N1ZV8yMDIxJywKICAgICAgICAnQ2VsbE1hcmtlcl9BdWdtZW50ZWRfMjAyMScsJ0F6aW11dGhfQ2VsbF9UeXBlc18yMDIxJykKCiMgZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gTlVMTCkKIyBjbHVzdGVyIDAKCk4xLmMwIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAwICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMwJGdlbmUKCk4xLmMwLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jMC5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmMwLkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzAuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKIyBjbHVzdGVyIDEKCk4xLmMxIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAxICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMxJGdlbmUKCk4xLmMxLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMxLkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jMS5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmMxLkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzEuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jMS5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgojIGNsdXN0ZXIgMTsgb2xmYWN0b3J5IGJ1bGIsIG5ldXJhbCBwbGF0ZSwgbWF5YmUgUmFkaWFsIEdsaWEsIApOMS5jMiA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMiAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jMiRnZW5lCgpOMS5jMi5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jMi5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMi5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMi5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jMi5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzIuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jMi5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmMyLkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzIuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDMKCk4xLmMzIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAzICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMzJGdlbmUKCk4xLmMzLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMzLkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jMy5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmMzLkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzMuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jMy5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgojIGNsdXN0ZXIgNAoKTjEuYzQgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDQgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzQkZ2VuZQoKTjEuYzQuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzQuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzQuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzQuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzQuRXJbWzRdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmM0LkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzQuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jNC5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgpOMS5Fci5nZW5lcy40IDwtIE4xLmM0LkVyW1s0XV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjQKCiMgY2x1c3RlciA1CgpOMS5jNSA8LSBDbHVzdGVyTWFya2VycyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gNSAmIGF2Z19sb2cyRkMgPiAwKQpnZW5lcyA8LSBOMS5jNSRnZW5lCgpOMS5jNS5FciA8LSBlbnJpY2hyKGdlbmVzLCBkYXRhYmFzZXMgPSBkYikKcGxvdEVucmljaChOMS5jNS5FcltbMV1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNS5FcltbMl1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNS5FcltbM11dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKcGxvdEVucmljaChOMS5jNS5FcltbNF1dLCBzaG93VGVybXMgPSAyMCwgbnVtQ2hhciA9IDQwLCB5ID0gIkNvdW50Iiwgb3JkZXJCeSA9ICJQLnZhbHVlIikKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzUuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jNS5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmM1LkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCk4xLkVyLmdlbmVzLjQgPC0gTjEuYzUuRXJbWzRdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuNAoKIyBjbHVzdGVyIDYKCk4xLmM2IDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA2ICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmM2JGdlbmUKCk4xLmM2LkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jNi5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmM2LkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzYuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jNi5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgoKIyBvdGhlciBjbHVzdGVycyAtIGNoYW5nZSB0aGUgY2x1c3RlciBudW1iZXIKTjEuYzYgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDExICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmM2JGdlbmUKCk4xLmM2LkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmM2LkVyW1s0XV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKTjEuRXIuZ2VuZXMuMSA8LSBOMS5jNi5FcltbMV1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4xCgpOMS5Fci5nZW5lcy4yIDwtIE4xLmM2LkVyW1syXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjIKCk4xLkVyLmdlbmVzLjMgPC0gTjEuYzYuRXJbWzNdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMwoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jNi5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgoKYGBgCgpjbHVzdGVyIDAgLSBhc3Ryb2N5dGUsIHJhZGlhbCBnbGlhLCBtaWNyb2dsaWEsIHN0cmlhdHVtIC0gdmVyeSBmZXcgZ2VuZXMgaW4gdGhlc2UgdGVybXMKY2x1c3RlciAxIC0gYWRyZW5hbCwgdGhhbG11cywgZW5kb3RoZWxpYWwsIGFzdHJvY3l0ZXMsIG5ldXJvbnMKY2x1c3RlcnMgMiAtIG5ldXJhbCBwbGF0ZSwgc3RyYXR1bSwgbmV1cm9ucywgTlBDLCBzdGVtLCBhc3RybyxuZXVyb2VuZG9jcmluZQpjbHVzdGVyIDMgLSBicmFpbiBtb2xlY3VsYXIgbGF5ZXIsIGVuZG90aGVsaWFsLCBhc3Ryb2N5dGUgZW1icnlvbmljLCAKY2x1c3RlciA0IC0gREcsIHN0cmlhdHVtIENBMywgR0FCQWVyZ2ljIG5ldXJvbnMKY2x1c3RlciA1IC0gREcsIG5ldXJvbnMsIEdsdXRhbWF0ZXJnaWMgbmV1cm9ucwpjbHVzdGVyIDYgLSBuZXVyb25zLCBhc3Ryb2N5dGUsIG1pY3JvZ2xpYSwgR0FCQW5ldXJvbnMKY2x1c3RlciA3IC0gbmV1cm9ucywgZ2x1dCBhbmQgZ2FiYQpjbHVzdGVyIDggLSBhc3Ryb2N5dGUsIGVuZG90aGVsaWFsLCBwZXJpY3l0ZQpjbHVzdGVyIDkgLSBlcGl0aGVsaWFsLCBlbWJyeW9uaWMgYXN0cm9jeXRlcywgR0FCQSBuZXVyb25zCmNsdXN0ZXIgMTAgLSBlcGl0aGVsaWFsCmNsdXN0ZXIgMTEgLSBlbmRvdGhlbGlhbCwgaW1tdW5lIGNlbGxzIFQgY2VsbHMKCgpFeHByZXNzaW9uIG9mIG1hcmtlcnMgZ2VuZXMgaW4gTmV1cm9uczIgCgpgYGB7cn0KCmZlYXR1cmVfbGlzdCA9IGMoIk1LSTY3IiwiU09YMiIsIlBPVTVGMSIsIkRMWDIiLCJQQVg2IiwiU09YOSIsIkhFUzEiLCJORVMiLCJSQkZPWDMiLCJNQVAyIiwiTkNBTTEiLCJDRDI0IiwiR1JJQTIiLCJHUklOMkIiLCJHQUJCUjEiLCJHQUQxIiwiR0FEMiIsIkdBQlJBMSIsIkdBQlJCMiIsIlRIIiwiQUxESDFBMSIsIkxNWDFCIiwiTlI0QTIiLCJDT1JJTiIsIkNBTEIxIiwiS0NOSjYiLCJDWENSNCIsIklUR0E2IiwiU0xDMUEzIiwiQ0Q0NCIsIkFRUDQiLCJTMTAwQiIsICJQREdGUkEiLCJPTElHMiIsIk1CUCIsIkNMRE4xMSIsIlZJTSIsIlZDQU0xIikKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QpICtSb3RhdGVkQXhpcygpCgpQRF9wb3VsaW4gPSBjKCJUSCIsIlNMQzZBMyIsIlNMQzE4QTIiLCJTT1g2IiwiTkRORiIsIlNOQ0ciLCJBTERIMUExIiwiQ0FMQjEiLCJUQUNSMiIsIlNMQzE3QTYiLCJTTEMzMkExIiwiT1RYMiIsIkdSUCIsIkxQTCIsIkNDSyIsIlZJUCIpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gUERfcG91bGluLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gUERfcG91bGluKStSb3RhdGVkQXhpcygpCgplYWxyeU5ldXIgPSBjKCJEQ1giLCJORVVST0QxIiwiVEJSMSIpCnByb2xpZmVyYXRpb24gPSBjKCJQQ05BIiwiTUtJNjciKQpuZXVyYWxzdGVtID0gYygiU09YMiIsIk5FUyIsIlBBWDYiLCJNQVNIMSIpCgpmZWF0dXJlX2xpc3QgPC0gYygiRENYIiwiTkVVUk9EMSIsIlRCUjEiLCJQQ05BIiwiTUtJNjciLCJTT1gyIiwiTkVTIiwiUEFYNiIsIk1BU0gxIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCkrUm90YXRlZEF4aXMoKQojIG5vIHByb2xpZmVyYXRpb24gbWFya2VyIGV4cHJlc3Npb24gIFBDTkEgb3IgTUtJNjcKIyBjbHVzdGVyIDQgREEgbmV1cm9ucyAtIHNob3dzIGVhcmx5IG5ldXJvbiBtYXJrZXIgYW5kIGxvdyBQQVggNAojIGNsdXN0ZXIgMyBoYXMgaGlnaGVyIFNPWDIgLSBuZXVyb2JsYXN0IG1hcmtlciAvIE5QQyBtYXJrZXIKCm1hdF9uZXVyb24gPSBjKCJSQkZPWDMiLCJTWVAiLCJETEc0NSIsIlZBTVAxIiwiVkFNUDIiLCJUVUJCMyIsIlNZVDEiLCJCU04iLCJIT01FUjEiLCJTTEMxN0E2IikgCiMgTmV1TiBpcyBGT1gzIC0gUkJGT1gzCiMgUFNEOTUgYWxzbyBTUC05MCBvciBETEc0CiMgVkdMVVQyIGlzIFNMQzE3QTYKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IG1hdF9uZXVyb24sIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKIyBjbHVzdGVyIDQgYWxzbyBzaG93IG1hdHVyZSBuZXVyb24gbWFya2VycwpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IG1hdF9uZXVyb24pK1JvdGF0ZWRBeGlzKCkKIyBleGNpdGF0b3J5IG5ldXJvbiBtYXJrZXJzCmV4ID0gYygiR1JJQTIiLCJHUklBMSIsIkdSSUE0IiwiR1JJTjEiLCJHUklOMkIiLCJHUklOMkEiLCJHUklOM0EiLCJHUklOMyIsIkdSSVAxIiwiQ0FNSzJBIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGV4LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZXgpK1JvdGF0ZWRBeGlzKCkKIyBpbmhpYml0b3J5IG5ldXJvbiBtYXJrZXJzCmluaCA9IGMoIkdBRDEiLCJHQUQyIiwgIkdBVDEiLCJQVkFMQiIsIkdBQlIyIiwiR0FCUjEiLCJHQlJSMSIsIkdBQlJCMiIsIkdBQlJCMSIsIkdBQlJCMyIsIkdBQlJBNiIsIkdBQlJBMSIsIkdBQlJBNCIsIlRSQUsyIikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGluaCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGluaCkrUm90YXRlZEF4aXMoKQojIGNsdXN0ZXIgNCBpcyBtb3JlIGV4Y2l0YXRvcnkgdGhhbiBpbmhiaXRvcnkgYnV0IG5laXRoZXIgbWFya2VyIHNldCBoYXMgbXVjaCBleHByZXNzaW9uIAoKIyMjIGdsaWEgbWFya2VycwptaWNyb2dsaWEgPSBjKCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiKSAgIyBBREdSRTEgaXMgYSBtaWNyb2dsaWEgbWFya2VyIEY0LzgwLCBDRDQ1IGlzIFBUUFJDLCBnZW5lIG5hbWUgSUJBMSBpcyBBSUYxCmFzdG9sZ05QQ3Byb21pY3JvID0gYygiR0ZBUCIsIlMxMDBCIiwiU0xDMUEyIiwiTUJQIiwiU09YMTAiLCJTUFAxIiwiRENYIiwiTkVVUk9EMSIsIlRCUjEiLCJQQ05BIiwiTUtJNjciLCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiKQojIG5vdGUgR0xUMSBpcyBFQUFUMiB3aGljaCBpcyBTTEMxQTIgZ2x1dGF0bWF0ZSB0cmFuc3BvcnRlcgojIGVwaXRoZWxpYWwKZXBpID0gYygiSEVTMSIsIkhFUzUiLCJTT1gyIiwiU09YMTAiLCJORVMiLCJDREgxIiwiTk9UQ0gxIikgIyBlLWNhZGhlcmluIGlzIENESDEKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBhc3RvbGdOUENwcm9taWNybywgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGFzdG9sZ05QQ3Byb21pY3JvLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKStSb3RhdGVkQXhpcygpCiMgY2x1c3RlciA0IGlzIG1vcmUgZXhjaXRhdG9yeSB0aGFuIGluaGJpdG9yeSBidXQgbmVpdGhlciBtYXJrZXIgc2V0IGhhcyBtdWNoIGV4cHJlc3Npb24gCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBlcGksIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBlcGksIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpK1JvdGF0ZWRBeGlzKCkKCiMgYWxzbyBhZGQgUmFkaWFsIGdsaWEgbWFya2VyIG92ZXJsYXAgd2l0aCBHbGlhIGFuZCBOZXVyb25zCgpmZWF0dXJlcyA8LSBjKCJQVFBSQyIsIkFJRjEiLCJBREdSRTEiLCAiVklNIiwgIlROQyIsIlBUUFJaMSIsIkZBTTEwN0EiLCJIT1BYIiwiTElGUiIsCiAgICAgICAgICAgICAgIklUR0I1IiwiSUw2U1QiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZXMsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlcywgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC42JykrUm90YXRlZEF4aXMoKQoKCmBgYAoKCkxhYmVsIHRoZSBOZXVyb24yIEZBQ1MgcG9wdWxhdGlvbiAKCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC42JwpjbHVzdGVyLmlkcyA8LSBjKCJOZXVyb25zMSIsImltbWF0dXJlTmV1cm9uczEiLCJOZXVyb25zMiIsCiAgICAgICAgICAgICAgICAgIk90aGVyIiwiREFuZXVyb25zMSIsIkRBbmV1cm9uczIiLCJpbW1hdHVyZU5ldXJvbnMyIiwKICAgICAgICAgICAgICAgICAiREFuZXVyb25zMyIsIlJHIiwiaW1tYXR1cmVOZXVyb25zMSIsIkVwaXRoZWxpYWwiLCJFbmRvdGhlbGlhbCIpCnVuaXF1ZShzZXUucSRSTkFfc25uX3Jlcy4wLjYpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRzdWJncm91cHMgPC0gSWRlbnRzKHNldS5xKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycsIHJlcGVsID0gVFJVRSkKCgojIGxhYmVsIGFnYWluIHdpdGgganVzdCBudW1iZXJpbmcKIyB0aGVuIEknbGwgZmluZCBtYXJrZXJzIGluIHBhaXJzIHRvIGRpc3Rpbmd1aXNoIGdyb3Vwcy4gCgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjYnCmNsdXN0ZXIuaWRzIDwtIGMoIk5ldXJvbnMxIiwiTmV1cm9uczIiLCJOZXVyb25zMyIsCiAgICAgICAgICAgICAgICAgIk90aGVyIiwiREFuZXVyb25zMSIsIkRBbmV1cm9uczIiLCJOZXVyb25zNCIsCiAgICAgICAgICAgICAgICAgIkRBbmV1cm9uczMiLCJSRyIsIk5ldXJvbnMyIiwiRXBpdGhlbGlhbCIsIkVuZG90aGVsaWFsIikKdW5pcXVlKHNldS5xJFJOQV9zbm5fcmVzLjAuNikKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1LnEpCnNldS5xIDwtIFJlbmFtZUlkZW50cyhzZXUucSwgY2x1c3Rlci5pZHMpCnNldS5xJG51bWJlci5ncm91cHMgPC0gSWRlbnRzKHNldS5xKQoKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ251bWJlci5ncm91cHMnLCByZXBlbCA9IFRSVUUpCgoKc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9OZXVyb25zMkxhYmVsc1NldTMwMDkyMDIyLlJEUyIpCgoKCmBgYAoKCgpGaW5kIG1hcmtlcnMgaW4gcGFpcnMgdG8gZ28gYmFjayBhbmQgY2xhc3NpZnkgdGhlIHN1Ymdyb3Vwcy4KV2lsbCBuZWVkIHRvIHJldHVybiB0byB0aGlzIGZvciBOZXVyb25zMSBGQUNTCgpOZXVyb25zIDUgYW5kIE5ldXJvbnMyIGhhZCBzaW1pbGFyIG1hcmtlcnMgYW5kIHdlcmUgbWVyZ2VkClN1YnNldCBhZ2FpbgoKYGBge3J9CgojIGZhc3RlciB0byBtYWtlIGEgc3Vic2V0IG9iamVjdHMgb2Ygb25seSBuZXVyb25zIGFuZCB1c2UgZmluZCBhbGwgbWFya2VycwoKbmV1cm9uLnN1YiA8LSBzdWJzZXQoc2V1LnEsIGlkZW50cyA9IGMoIk5ldXJvbnMxIiwiTmV1cm9uczIiLCJOZXVyb25zMyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOZXVyb25zNCIpKQoKbmV1cm9uLnN1Yi5tYXJrZXJzIDwtIEZpbmRBbGxNYXJrZXJzKG5ldXJvbi5zdWIpCgp0b3A1IDwtIG5ldXJvbi5zdWIubWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG49NSwgd3QgPSBhdmdfbG9nMkZDKQpEb0hlYXRtYXAobmV1cm9uLnN1YiwgZmVhdHVyZXMgPSB0b3A1JGdlbmUsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKCkRvdFBsb3QobmV1cm9uLnN1YiwgZmVhdHVyZXMgPSB0b3A1JGdlbmUpICsgUm90YXRlZEF4aXMoKQoKCiMgbmV1cm9ucyA1IHdhcyBqb2luZWQgdG8gTmV1cm9ucyAyCiMgY2x1c3RlciBtYXJrZXJzIGRvbmUgYWdhaW4KCiMgTmV1cm9uczEgOiBNR1AgKHRhcmdldGluZyBuZXVyYWwgcHJvamVjdGlvbnMgQk1QIHNpZ25hbGluZyksIEhQRCAoZXhjaXRhdG9yeSBhbmQgaW5pYml0b3J5LCBjb3VsZCBiZSBjZWxsIGFkaGVzaW9uIG9yIGFwb3B0b3NpcyksIE1TWDEgKHRyYW5zY3JpcHRpb24gZmFjdG9yIEJNUCBzaWduYWxpbmcsIG1pZGJyYWluIG1hcmtlciwgZGV2ZWxvcG1lbnRhbCksIENZUDFCMSAocmVkb3ggaG9tZW9zdGF0aXMpICAgCiMgTUdQLCBIUEQsIE1TWDEsIENZUDFCMQoKIyB0aGVyZSBpc24ndCBhIGdvb2QgcHJvcG9ydGlvbiBvZiBjZWxscyBleHByZXNzaW9uIGFueSBvZiB0aGUgbWFya2VycywgTmV1cm9uczIgaGFzIHRoZSBiZXN0IGFtb3VudAoKIyBOZXVyb25zMjogVEZQMTIgc2VyaW5lIHByb3RlYXNlIG1lbGF0b25pbiBjb252ZXJzaW9uLCBQVE4gKGN5dG9raW5lIHNpZ25hbGluZyksIExZZ0ggKGluaGFuY2VzIG5BQ2hScyksIFMxMDBBMTAgKG1vZHVsYXRlcyBzZXJvdG9uaW4gcmVjZXB0b3IpLCBJRkkyNyAoYW50aXZpcmFsIGFjdGl2aXR5KQojVEZQMTIsUFROLCBMWTZILCBTMTAwQTEwLCBJRkkyNyAKCiMgTmV1cm9uczM6IFNPWDQsIEFTQ0wxIChuZXVyb2dlbmVzaXMpLAoKIyBTT1g0IGlzIGEgbWFya2VyIG9mIDMgYnV0IGhhcyBoaWdoIGV4cHJlc3Npb24gaW4gNCBhcyB3ZWxsIAoKIyBOZXVyb25zNDogUENBVDQgKG5vdCBub3RlZCBpbiBuZXVyb25zKSwgVFBIMSAoNUhUIHN5bnRoZXNpcyksIEdLNSAobmV1cm9uYWwgbWFpbnRhaW5hbmNlKSwgU1NUIChzb21hdG9zdGF0aW4sIEdBQkEgc3Bpa2UgcmVndWxhdGlvbiksIFRUUiAobmV1cmFsIHByb3RlY3RpdmUgaW4gQUQpCiMgUENBVDQsIFRQSDEsIEdLNSwgU1NULCBUVFIKCiMgbWFya2VycyB0byB1c2UKCiMgTmV1cm9uczE6IE1TWDEsIENZUDFCMSAgICAgIAojIE5ldXJvbnMyOiBMWTZILCBTMTAwQTEwICAgICAKIyBOZXVyb25zMzogU09YNCwgQVNDTDEgKE5ldXJvZ2VuZXNpcykgSW1tYXR1cmUKIyBOZXVyb25zNDogR0s1LCBTU1QgICAgICAgICAgICAgICAgICAgICAgICAgTW9yZSBtYXR1cmUKCgoKIyBNYXliZSBuZXVyb25zIDEgYW5kIDIgY291bGQgYmUgbWVyZ2VkCgojIGxldHMgc2VlIGhvdyB0aGUgbWFya2VycyB3b3VsZCBsb29rCgpuZXVyb25zLjFhbmQyIDwtIEZpbmRNYXJrZXJzKG5ldXJvbi5zdWIsIGlkZW50LjEgPSBjKCJOZXVyb25zMSIsIk5ldXJvbnMyIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWRlbnQuMiA9IGMoIk5ldXJvbnMzIiwiTmV1cm9uczQiKSkKCnRvcDEwIDwtIG5ldXJvbnMuMWFuZDIgJT4lIHRvcF9uKG49MTAsIHd0ID0gYXZnX2xvZzJGQykKZnQudXAgPC0gcm93bmFtZXModG9wMTApICMgdXAgaW4gTmV1cm9uczEgYW5kIDMKdG9wMTAgPC0gbmV1cm9ucy4xYW5kMiAlPiUgdG9wX24obj0tMTAsIHd0ID0gYXZnX2xvZzJGQykKZnQuZG93biA8LSByb3duYW1lcyh0b3AxMCkKZmVhdHVyZXMgPC0gYyhmdC51cCxmdC5kb3duKQoKRG9IZWF0bWFwKG5ldXJvbi5zdWIsIGZlYXR1cmVzID0gZmVhdHVyZXMsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKCkRvdFBsb3QobmV1cm9uLnN1YiwgZmVhdHVyZXMgPSBmZWF0dXJlcykgKyBSb3RhdGVkQXhpcygpCgojIGFsbCB0aGUgbWFya2VycyB3ZXJlIHVwIHJlZ3VsYXRlZCBpbiBuZXVyb25zMiBhbmQgbm90IHJlYWxseSBuZXVyb25zMQojIEknbGwga2VlcCB0aGVtIHNlcGFyYXRlZAoKCmBgYAoKClVzZSBzdWJncm91cGluZyBhbmQgZmluZCBjbHVzdGVyIG1hcmtlcnMgdG8gbG9vayBhdCBuZXVyb25hbCBzdWJ0eXBlcy4KCmBgYHtyfQoKIyBmYXN0ZXIgdG8gbWFrZSBhIHN1YnNldCBvYmplY3RzIG9mIG9ubHkgbmV1cm9ucyBhbmQgdXNlIGZpbmQgYWxsIG1hcmtlcnMKCm5ldXJvbi5zdWIgPC0gc3Vic2V0KHNldS5xLCBpZGVudHMgPSBjKCJEQW5ldXJvbnMxIiwiREFuZXVyb25zMiIsIkRBbmV1cm9uczMiKSkKCm5ldXJvbi5zdWIubWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhuZXVyb24uc3ViKQoKdG9wNSA8LSBuZXVyb24uc3ViLm1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuPTUsIHd0ID0gYXZnX2xvZzJGQykKRG9IZWF0bWFwKG5ldXJvbi5zdWIsIGZlYXR1cmVzID0gdG9wNSRnZW5lLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCgpEb3RQbG90KG5ldXJvbi5zdWIsIGZlYXR1cmVzID0gdG9wNSRnZW5lKSArIFJvdGF0ZWRBeGlzKCkKCiMgbWFya2VycyBhcmUgbXVjaCBjbGVhcmVyIGZvciB0aGUgREEgbmV1cm9uIHN1Ymdyb3VwcwoKIyBEQSBuZXVyb25zIDE6IFdJRjEsIENZUDFCMSwgSUdGQlAzLCBIUEQsIFdGSUtLTjIKIyBXSUYxIChzZWNyZXRlZCBXTlQgaW5oaWJpdG9yLCBwcm9tb3RlcyByZWdlbmVyYXRpb24pLCBDWVAxQjEgKHJlZG94IGhvbWVvc3RhdGlzKSwgSUdGQlAzIChwcm9sYWN0aW4gc2VjcmV0aW9uIHJlZ3VsYXRpb24gaHlwb3RoYWxtdXMpLCBIUEQgKG5ldXJvIHByb3RlY3RpdmUpLCBXRklLS04yIChSZWNlcHRvciBmb3IgVE5DKQojIERBIG5ldXJvbnMyOiBDREg3LCBSVU5YMVQxLCBBU0NMMSwgRExLMSwgTUVHMwojIENESDcgKG5ldXJvIGNpcmN1aXRyeSBkZXZlbG9wbWVudCwgU0VNQSksIFJVTlgxVDEgKG5ldXJvbmFsIGRpZmZlcmVudGlhdGlvbiksIEFTQ0wxIChuZXVyb25hbCBkaWZmZXJlbnRpYXRpb24pLCBETEsxIChuZXVyYWwgZGlmZmVyZW50YXRpb24pLCBNRUczIChuZXVyYWwgaG9tZW9zdGF0aXMpICAKIyBEQSBuZXVyb25zMzogUENBVDQsIE5FVVJPRDEsIE5DS0FQNSwgR0s1LCBTU1QKIyBQQ0FUNCAoZGVuZHJpdGljIGdyb3d0aCksIE5FVVJPRDEgKG5ldXJhbCBkaWZmZXJlbnRhdGlvbiksIE5DS0FQNSAoRXhjaXRvcnkgbmV1cm9ucyksIEdLNSAoVEggKSwgU1NUIChyZWd1YWxhdGVzIHNwaWtlIHRpbWVzKQoKIyBEQSBuZXVyb25zMTogQ1lQMUIxLCBJR0ZCUDMKIyBEQSBuZXVyb25zMjogUlVOWDFUMSwgQVNDTDEKIyBEQSBuZXVyb25zMzogTkVVUk9EMSwgTkNLQVA1CgoKYGBgCgoKTmFtZSB0aGUgTmV1cm9uczIgRkFDUyBwb3B1bGF0aW9uIHdpdGggdGhlIE5ldXJvbiBzdWJ0eXBlIGxhdHRlci4KCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC42JwpjbHVzdGVyLmlkcyA8LSBjKCJOZXVyb25zLU1TWDEiLCJOZXVyb25zLUxZNkgiLCJOZXVyb25zLUFTQ0wxIiwKICAgICAgICAgICAgICAgICAiT3RoZXIiLCJEQW5ldXJvbnMtQ1lQMUIxIiwiREFuZXVyb25zLUFTQ0wxIiwiTmV1cm9ucy1HSzUiLAogICAgICAgICAgICAgICAgICJEQW5ldXJvbnMtTkVVUk9EMSIsIlJHIiwiTmV1cm9ucy1MWTZIIiwiRXBpdGhlbGlhbCIsIkVuZG90aGVsaWFsIikKCnVuaXF1ZShzZXUucSRSTkFfc25uX3Jlcy4wLjYpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRjZWxsc3ViZ3JvdXBzIDwtIElkZW50cyhzZXUucSkKCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdjZWxsc3ViZ3JvdXBzJywgcmVwZWwgPSBUUlVFKQoKCmBgYAoKCglwcmVkdWN0aW9uIHN1bW1hcnkJQ2VsbF9UeXBlcwowCW5ldXJvbnMJTmV1cm9ucwoxCU5QQy9vbGlnby9hc3Ryby9uZXVyb25zL1JHCU5ldXJvbnMKMgluZXVyb25zCU5ldXJvbnMKMwlOUEMvUkcvQXN0cm8JTmV1cm9ucwo0CVJHL2VuZG8JUkcKNQlEQSBuZXVyb25zCU5ldXJvbnMKNglOZXVyb25zCU5ldXJvbnMKNwlEQSBuZXVyb25zCU5ldXJvbnMKOAlSRy9lbmRvCUVuZG90aGVsaWFsCjkJUkcvYXN0cm8JQXN0cm8KMTAJb3BjL25wYy9hc3Ryby9uZXVyb25zCU5ldXJvbnMKMTEJbmV1cm9ucy9kaXZpZGluZy9SRwlOZXVyb25zCgpDbHVzdGVyaW5nIGhpZ2hlciByZXMKcmVzIDEuMgkKMAlOZXVyb25zCjEJTmV1cm9ucwoyCU5ldXJvbnMKMwlBc3Ryby9SRy9OZXVyb25zCjQJUkcvQXN0cm8vTmV1cm9ucwo1CVJHL05ldXJvbnMvTlBDCjYJUkcvTlBDL0VuZG8KNwlOZXVyb25zIERBCjgJTmV1cm9ucwo5CU5ldXJvbnMgREEKMTAJTmV1cm9ucyBEQQoxMQlSRy9FbmRvCjEyCVJHL0FzdHJvIAoxMwlSRy9Bc3Ryby9OZXVyb25zCjE0CU5ldXJvbnMvUkcKCgoKCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMS4yJwojIGhpZ2hsaWdodCB0aGUgREEgbmV1cm9ucwpjbHVzdGVyLmlkcyA8LSBjKCJOZXVyb25zIiwiTmV1cm9ucyIsIk5QQyIsCiAgICAgICAgICAgICAgICAgIk5ldXJvbnMiLCJOZXVyb25zIiwiTmV1cm9ucyIsCiAgICAgICAgICAgICAgICAgIk5ldXJvbnMgREEiLCJOZXVyb25zIiwiTmV1cm9ucyBEQSIsCiAgICAgICAgICAgICAgICAgIk5ldXJvbnMgREEiLCAiTmV1cm9ucyBEQSIsIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAiUkcvQXN0cm8iLCJSRy9Bc3Ryby9OZXVyb25zIiwiTmV1cm9ucy9SRyIpCmNsdXN0ZXIuaWRzIDwtIGMoIk5ldXJvbnMiLCJOZXVyb25zIiwiTmV1cm9ucyIsCiAgICAgICAgICAgICAgICAgIk5ldXJvbnMiLCJOZXVyb25zIiwiTlBDIiwKICAgICAgICAgICAgICAgICAiTmV1cm9ucyIsIk5ldXJvbnMiLCJOZXVyb25zIiwKICAgICAgICAgICAgICAgICAiTmV1cm9ucyIsICJOZXVyb25zIiwiRW5kb3RoZWxpYWwiLAogICAgICAgICAgICAgICAgICJBc3Ryb2N5dGVzIiwiUmFkaWFsIEdsaWEiLCJSYWRpYWwgR2xpYSIpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRDZWxsX1R5cGVzIDwtIElkZW50cyhzZXUucSkKCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdDZWxsX1R5cGVzJywgcmVwZWwgPSBUUlVFKQojRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjEuMicsIHJlcGVsID0gVFJVRSkKCiNjbHVzdHJlZShzZXUucSkKCgpgYGAKCgpgYGB7cn0KCiMgc2F2ZSB0aGUgTmV1cm9uczIgd2l0aCBsYWJlbHMKCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvTmV1cm9uczJMYWJlbHNTZXUzMDA5MjAyMi5SRFMiKQoKYGBgCgoKRkFDUyBwb3B1bGF0aW9uIEdsaWExIChzaG91bGQgYmUgYXN0cm9jeXRlcykKCmBgYHtyfQojIGV4cGxvcmUgZmlsdGVyaW5nCnNldSA8LSBHbGlhMQpzZXUKIyAKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsICJuQ291bnRfUk5BIiwgInBlcmNlbnQubXQiKSwgbmNvbCA9IDMpCgpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiksIHkubWF4ID0gMTAwMCkKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIpLCB5Lm1heCA9IDUwMCkKVmxuUGxvdChzZXUsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5Db3VudF9STkEiKSwgeS5tYXggPSAyMDAwKQoKIyBmaWx0ZXIgbW9yZSBjZWxscwoKc2V1LmZ0IDwtIHN1YnNldChzZXUsIHN1YnNldCA9IG5GZWF0dXJlX1JOQSA+IDMwMCAmIG5Db3VudF9STkEgPiA1MDAgJiBuQ291bnRfUk5BIDwgMTAwMDApIApzZXUuZnQKCiMgc3RpbGwgYSBsb3Qgb2YgY2VsbHMgNDcyOTUKIyB3aWxsIGxpa2VseSByZW1vdmUgYSBsb3QgbW9yZSB3aXRoIHRoZSBkb3VibGV0IGZpbmRlcgoKCmBgYAoKYGBge3J9CgpzYXZlUkRTKHNldS5mdCwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMUFzdHJvU2V1MDExMDIwMjIuUkRTIikKCnNldS5mdCA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTFBc3Ryb1NldTAxMTAyMDIyLlJEUyIpCgpzZXUuZnQgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWExQXN0cm9TZXUwMTEwMjAyMi5SRFMiKQoKYGBgCgpEb3VibGV0IGZpbmRlcgoKYGBge3J9CgpzdXBwcmVzc01lc3NhZ2VzKHJlcXVpcmUoRG91YmxldEZpbmRlcikpCgojIGZpbHRlcmluZyBvdXQgTUFMQVQxIGFuZCBtaXRvY2hvbmRyaWFsIGdlbmVzCgpzZXUuZnQgPC0gc2V1LmZ0WyFncmVwbCgiTUFMQVQxIiwgcm93bmFtZXMoc2V1LmZ0KSksIF0Kc2V1LmZ0IDwtIHNldS5mdFshZ3JlcGwoIl5NVC0iLCByb3duYW1lcyhzZXUuZnQpKSwgXQoKIyBsaWtlIGluIHRoZSB0dXRvcmlhbCBJJ20gZm9sbG93aW5nIE1BTEFUMSBpcyB0aGUgdG9wIG1vc3QgZXhwcmVzc2VkIGdlbmUuICBUaGUgdG9wIGdlbmVzIGFyZSBhIGxvdCBvZiBNVCBhbmQgUmlib3NvbWFsIGdlbmVzCgpzZXUuZnRbWyJwZXJjZW50LnJiIl1dIDwtIFBlcmNlbnRhZ2VGZWF0dXJlU2V0KHNldS5mdCwgcGF0dGVybiA9ICJeUlAiKQoKIyBkb3duIHNhbXBsZSB0aGVyZSBhcmUgdG9vIG1hbnkgY2VsbHMgdG8gcnVuIGRvdWJsZXQgZmluZGVyCnNldS5zdWIgPC0gc3Vic2V0KHNldS5mdCwgZG93bnNhbXBsZSA9IDIwMDAwKQoKc2V1LmQgPSBOb3JtYWxpemVEYXRhKHNldS5zdWIpCnNldS5kID0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1LmQsIHZlcmJvc2UgPSBGKQpzZXUuZCA9IFNjYWxlRGF0YShzZXUuZCwgdmFycy50by5yZWdyZXNzID0gYygibkZlYXR1cmVfUk5BIiwgInBlcmNlbnQubXQiKSwKICAgIHZlcmJvc2UgPSBGKQpzZXUuZCA9IFJ1blBDQShzZXUuZCwgdmVyYm9zZSA9IEYsIG5wY3MgPSAxNSkKc2V1LmQgPSBSdW5VTUFQKHNldS5kLCBkaW1zID0gMToxMCwgdmVyYm9zZSA9IEYpCgpuRXhwIDwtIHJvdW5kKG5jb2woc2V1LmQpICogMC4xNSkgICMgZXhwZWN0IG1vcmUgZG91YmxldHMgYmVjYXVzZSB0aGVyZSBpcyBhIGxvdCBtb3JlIGNlbGxzCnNldS5kIDwtIGRvdWJsZXRGaW5kZXJfdjMoc2V1LmQsIHBOID0gMC4yNSwgcEsgPSAwLjA5LCBuRXhwID0gbkV4cCwgUENzID0gMToxMCkKIyB0aGUgbWVtb3J5IGxpbWl0IGlzIHJlYWNoZWQgaGVyZSAtIEkgY291bGQgcnVuIG9uIGNvbXB1dGUgY2FuYWRhCiMgRm9yIG5vdyBJJ2xsIGRvd25zYW1wbGUKIyB0aGlzIHdvcmtzCgojIG5hbWUgb2YgdGhlIERGIHByZWRpY3Rpb24gY2FuIGNoYW5nZSwgc28gZXh0cmFjdCB0aGUgY29ycmVjdCBjb2x1bW4gbmFtZS4KREYubmFtZSA9IGNvbG5hbWVzKHNldS5kQG1ldGEuZGF0YSlbZ3JlcGwoIkRGLmNsYXNzaWZpY2F0aW9uIiwgY29sbmFtZXMoc2V1LmRAbWV0YS5kYXRhKSldCgoKY293cGxvdDo6cGxvdF9ncmlkKG5jb2wgPSAyLCBEaW1QbG90KHNldS5kLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikgKyBOb0F4ZXMoKSwKICAgIERpbVBsb3Qoc2V1LmQsIGdyb3VwLmJ5ID0gREYubmFtZSkgKyBOb0F4ZXMoKSkKClZsblBsb3Qoc2V1LmQsIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIGdyb3VwLmJ5ID0gREYubmFtZSwgcHQuc2l6ZSA9IDAuMSkKCmBgYAoKCgoKUmVtb3ZlIHRoZSBkb3VibGV0IGNlbGxzCgpgYGB7cn0Kc2V1LmQgPC0gc2V1LmRbLCBzZXUuZEBtZXRhLmRhdGFbLCBERi5uYW1lXT09ICJTaW5nbGV0Il0KZGltKHNldS5kKQpkaW0oc2V1LnN1YikKCiMgMjAwMDAgcHJlIGZpbHRlcgojIGNyZWF0ZXMgdGhlIGV4cGVjdGVkIHBlcmNlbnRhZ2UKCmBgYAoKUmVwZWF0IHdvcmtmbG93IHdpdGggZG91YmxldCByZW1vdmVkIGRhdGEgYW5kIGZpbmQgY2x1c3RlcnMgZm9yIAoKYGBge3J9CnNldSA8LSBOb3JtYWxpemVEYXRhKHNldS5kLCBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLCBzY2FsZS5mYWN0b3IgPSAxMDAwMCkKc2V1IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldSwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAyMDAwKQpzZXUgPC0gU2NhbGVEYXRhKHNldSkKc2V1IDwtIFJ1blBDQShzZXUpCnNldSA8LSBSdW5VTUFQKHNldSwgcmVkdWN0aW9uID0gInBjYSIsIG4ubmVpZ2hib3JzID0gNDMsIGRpbXMgPSAxOjMwKQpEaW1QbG90KHNldSwgcmVkdWN0aW9uID0gInVtYXAiKQoKc2V1LnEgPC0gRmluZE5laWdoYm9ycyhzZXUsIGRpbXMgPSAxOjI1LCBrLnBhcmFtID0gNDMpCnNldS5xIDwtIEZpbmRDbHVzdGVycyhzZXUucSwgcmVzb2x1dGlvbiA9IGMoMCwwLjIsMC40LDAuNikpCnNldS5xIDwtIEZpbmRDbHVzdGVycyhzZXUucSwgcmVzb2x1dGlvbiA9IGMoMCwwLjA1LDAuMSwwLjgpKQpsaWJyYXJ5KGNsdXN0cmVlKQpjbHVzdHJlZShzZXUucSkKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjA1JykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjEnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMicpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC40JykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjYnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuOCcpCgoKCgpgYGAKTG9vayBhdCBzb21lIGV4cHJlc3Npb24gbWFya2VycyBpbiBhIGZlYXR1cmUgcGxvdAoKYGBge3J9CiMgZ2VuZXMgcmVwb3J0ZWQgdXAgaW4gQXN0cm9jeXRlcwpGZWF0dXJlUGxvdChzZXUucSwgZmVhdHVyZXMgPSBjKCJHRkFQIiwiUzEwMEIiLCJBUVA0IiwiU0xDMUEzIiwiR0pBMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFQT0UiLCJURUFEMSIsIkdTVEE0IiwiU09YOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlZJTSIsIkhNRzIwQSIsIkFMREgxTDEiKSkKIyBhbG1vc3Qgbm8gR0ZBUCBleHByZXNzaW9uIGFuZCBsb3RzIG9mIFMxMDBCIGV2ZXJ5d2hlcmUKCmBgYAoKCgpQcmVkaWN0IGNlbGwgdHlwZXMKCmBgYHtyfQoKCiMgU05DQSBhbmQgY29udHJvbCBtaWRicmFpbiBvcmdhbm9pZHMgMTY1IGRheXMgaW4gY3VsdHVyZQpNQk8gPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQVNUMjNfQnJhaW5Db21tL01CT2NsdXN0ZXJzX25hbWVzMjkwNzIwMjEucmRzIikKCiMgTWlkYnJhaW4gIEFJVzAwMiAxMjAgZGF5cyBpbiBjdWx0dXJlCkFJV01CTyA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9BSVd0cmlvMTIwZGF5cy9NT2ludGVncmF0ZWRDbHVzdGVySzEyM3JlczAuOC5uYW1lc19ub3YxNl8yMDIxIikKCiMgTWlkYnJhaW4gQUlXMDAyIDYwIGRheXMgaW4gY3VsdHVyZQoKQUlXNjAgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQUlXdHJpbzYwZGF5cy9BV0kwMDJQYXJraW5LT1BpbmtLTzYwZGF5c19sYWJlbHNfMTQwNTIwMjIucmRzIikKCgojZmlyc3QgcHJlZGljdCB3aXRoIHRoZSBNQk8gZGF0YQpJZGVudHMoTUJPKSA8LSAiY2x1c3Rlcl9sYWJlbHMiCkRlZmF1bHRBc3NheShNQk8pIDwtICJSTkEiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCnByaW50KCJmaW5kaW5nIHJlZmVyZW5jZSBhbmNob3JzIikKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IE1CTyAscXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBNQk8kY2x1c3Rlcl9sYWJlbHMpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJE1CT0FTVDIzLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdNQk9BU1QyMy5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuOCwgc2V1LnEkTUJPQVNUMjMucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBjbHVzdGVycyBkb24ndCBicmVhayB1cCBieSB0aGUgcHJlZGljdGVkIGNlbGwgdHlwZXMKCiMjIyMjIyMjIyMjIyBhbm90aGVyIHByZWRpY3Rpb25zIG5vdyB1c2luZyB0aGUgQUlXIG9yZ2Fub2lkcwoKSWRlbnRzKEFJV01CTykgPC0gInJlczA4bmFtZXMiCkRlZmF1bHRBc3NheShBSVdNQk8pIDwtICJSTkEiCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gQUlXTUJPICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IEFJV01CTyRyZXMwOG5hbWVzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRNQk9BSVcucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ01CT0FJVy5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuOCwgc2V1LnEkTUJPQUlXLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgdGhlIHByZWRpY3RlZCBjZWxsIHR5cGVzIG1ha2UgbW9yZSBzZW5zZSBmcm9tIHRoZSBBSVcwMDIgb3JnYW5vaWQKIyBub3cgcHJlZGljdCB3aXRoIHRoZSBBSVcwMDIgNjAgZGF5cyBvcmdhbm9pZAoKSWRlbnRzKEFJVzYwKSA8LSAiY2x1c3Rlci5pZHMiCkRlZmF1bHRBc3NheShBSVc2MCkgPC0gIlJOQSIKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBBSVc2MCwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBBSVc2MCRjbHVzdGVyLmlkcykgCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJEFJVzYwLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdBSVc2MC5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuOCwgc2V1LnEkQUlXNjAucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBzYXZlIG9qYmVjdCB3aXRoIHByZWRpY2l0b25zCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTFQcmVkaWN0aW9uc1NldTAxMTAyMDIyLlJEUyIpCgoKCgpgYGAKCgpQcmVkaWN0IHdpdGggdGhlIGJyYWluIHNjUk5Bc2VxCgpgYGB7cn0KCnBhdGh3YXkgPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy8iCnNldS5xIDwtIHJlYWRSRFMocGFzdGUocGF0aHdheSwiR2xpYTFMYWJsZWRTZXUzMDExMDIwMjIuUkRTIixzZXAgPSAiIikpCgoKIyByZWFkIGluIHRoZSByZWZlcmVuY2UgZGF0YXNldAojIGZyb20gQmhhZHVyaSBtaWRicmFpbiBhbmQgc3RyaWF0dW0Kc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9CaGFkdXJpX3dob2xlQnJhaW4vQmhhZHVyaV9taWRicmFpbl9zdHJpYXR1bS5SRFMiKQoKSWRlbnRzKHNldS5yKSA8LSAiY2VsbF9jbHVzdGVyIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gc2V1LnIsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gc2V1LnIkY2VsbF9jbHVzdGVyKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQmhhLm1pZC5zdHJpLnByZWQgPC0gSWRlbnRzKHNldS5xKQpwcmludCh0YWJsZShzZXUucSRCaGEubWlkLnN0cmkucHJlZCkpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5taWQuc3RyaS5wcmVkJykKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnc3ViZ3JvdXBzJykKCiMgZG8gdGhlIHByZWRpY3Rpb25zIGRpZmZlciB3aXRoIHRoZSBtYWluIGNlbGwgdHlwZSBncm91cHMgaW5zdGVhZCBvZiB0aGUgY2x1c3RlciBpbiB0aGUgcmVmZXJlbmNlIGRhdGE/IApJZGVudHMoc2V1LnIpIDwtICJjZWxsX3R5cGUiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX3R5cGUpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdwcmVkaWN0ZWQuaWQnKQoKYGBgCgoKCgoKYGBge3J9CgojIEFJVzAwMiAxMjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkTUJPQUlXLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3MTIwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXcxMjApIDwtIE5VTEwKZGYudG9wLmFpdzEyMCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKQoKIyBBSVcwMDIgNjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkQUlXNjAucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCA8LWFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3NjAgPC0gdG9wLnByZWQuY2VsbHR5cGUuQUlXNjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFpdzYwKSA8LSBOVUxMCmRmLnRvcC5haXc2MCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3NjApCgoKIyBBU1QyMyAxNjUgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkTUJPQVNUMjMucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkFTVDIzIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykgPC0gTlVMTApkZi50b3AuQVNUMjMkSSA8LSByb3cubmFtZXMoZGYudG9wLkFTVDIzKQoKIyMjIGFkZCBpbiB0aGUgcHJlZGljdGlvbiBmcm9tIGJyYWluIGRhdGEgQmhhZHVyaSBtaWRicmFpbiBhbmQgc3RyaWF0dW0KdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJEJoYS5taWQuc3RyaS5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkJoYSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkJoYSA8LSB0b3AucHJlZC5jZWxsdHlwZS5CaGFbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQmhhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkJoYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQmhhKSA8LSBOVUxMCmRmLnRvcC5CaGEkSSA8LSByb3cubmFtZXMoZGYudG9wLkJoYSkKCiMjIHRoZXNlIGFyZSBjYWxjdWxhdGVkIGJlbG93CiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiB3aG9sZSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gZG93biBzYW1wbGVkCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRCaGEpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQmhhMSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkJoYTEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhMVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5CaGEkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQmhhJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5CaGExKSA8LSBOVUxMCmRmLnRvcC5CaGExJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGExKQoKcHJlZC50YWJsZSA8LSBtZXJnZShkZi50b3AuQVNUMjMsIGRmLnRvcC5haXc2MCwgYnkgPSAnSScsIGFsbCA9IFRSVUUpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLmFpdzEyMCwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYSwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYTEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlCgpgYGAKVGhlc2UgcHJlZGljdGlvbnMgYXJlIG5vdCBnb29kLiAgVGhlcmUgYXJlIHNldmVyYWwgYXN0cm9jeXRlIG1hcmtlcnMgYnkgZXhwcmVzc2lvbiBsZXZlbHMuICBFdmVyeXRoaW5nIGlzIHByZWRpY3RlZCBhcyBSYWRpYWwgZ2xpYSBvciBvbGlnbyBkZW5kcm9jeXRlcwoKClRyeSB0byBwcmVkaWN0IHdpdGggdGhlIHdob2xlIGJyYWluIGFuZCBzZWUgaWYgaXQncyBkaWZmZXJlbnQKCmBgYHtyfQojIHJlYWQgaW4gdGhlIHJlZmVyZW5jZSBkYXRhc2V0CiMgZnJvbSBCaGFkdXJpIG1pZGJyYWluIGFuZCBzdHJpYXR1bQpzZXUuciA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QdWJsaWNEYXRhL0JoYWR1cmlfd2hvbGVCcmFpbi9CaGFkdXJpX2Rvd25zYW1wbGUuUkRTIikKCklkZW50cyhzZXUucikgPC0gImNlbGxfY2x1c3RlciIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfY2x1c3RlcikKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYSA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJEJoYSkpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYScpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycpCgoKCgoKYGBgCgoKCgpUcnkgdG8gcHJlZGljdCB3aXRoIHRoZSBhc3Ryb2N5dGUgS2FtYXRoIGRhdGEKCmBgYHtyfQoKYXN0cm8ucmVmIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL01hY29za29fRGF0YS9QRF9hc3Ryby5SZHMiKQojIG5lZWQgdG8gbWFrZSBQQ0EgYW5kIFVNQVAKYXN0cm8ucmVmIDwtIE5vcm1hbGl6ZURhdGEoYXN0cm8ucmVmKQphc3Ryby5yZWYgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoYXN0cm8ucmVmLCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCmFzdHJvLnJlZiA8LSBTY2FsZURhdGEoYXN0cm8ucmVmKQphc3Ryby5yZWYgPC0gUnVuUENBKGFzdHJvLnJlZikKYXN0cm8ucmVmIDwtIFJ1blVNQVAoYXN0cm8ucmVmLCByZWR1Y3Rpb24gPSAicGNhIiwgbi5uZWlnaGJvcnMgPSAyMDUsIGRpbXMgPSAxOjI1KQoKY29sbmFtZXMoYXN0cm8ucmVmQG1ldGEuZGF0YSkKCgpJZGVudHMoYXN0cm8ucmVmKSA8LSAiQ2VsbF9TdWJ0eXBlIgpEZWZhdWx0QXNzYXkoYXN0cm8ucmVmKSA8LSAiUk5BIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwpwcmludCgiZmluZGluZyByZWZlcmVuY2UgYW5jaG9ycyIpCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBhc3Ryby5yZWYgLHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjIwKQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gYXN0cm8ucmVmJENlbGxfU3VidHlwZSwgay53ZWlnaHQgPSAxMCkKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCiMgYWRkIG5ldyBkYXRhc2xvdCBmb3IgTUJPIHByZWRpY3RlZCBJRCB0byBtYWtlIHRoZSBuZXh0IHByZWRpY3Rpb24Kc2V1LnEkYXN0cm8ucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ2FzdHJvLnByZWQnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJGFzdHJvLnByZWQpCgpzZXUucSRwcmVkaWN0ZWQuaWQgPC0gaWZlbHNlKHNldS5xJHByZWRpY3Rpb24uc2NvcmUubWF4ID4gMC45NSwgc2V1LnEkcHJlZGljdGVkLmlkLCBOQSkKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkYXN0cm8ucHJlZC50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdhc3Ryby5wcmVkLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkYXN0cm8ucHJlZC50aHJlc2gpCgojIDE5OTg2IEFzdHJvX1ZJTV9UTkZTUkYxMkEgbm8gdGhyZXNob2xkICAgICAgQXN0cm9fR0xZQVRMMiAxNAojIDgzNzYgQXN0cm9fVklNX1RORlNSRjEyQSAgIHdpdGggOTUlIHRocmVzaG9sZAoKCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRhc3Ryby5wcmVkLnRocmVzaCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5hc3RybyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFzdHJvIDwtIHRvcC5wcmVkLmFzdHJvW29yZGVyKHRvcC5wcmVkLmFzdHJvJFZhcjEsLXRvcC5wcmVkLmFzdHJvJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5hc3RybykgPC0gTlVMTAoKCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRhc3Ryby5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmFzdHJvIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYXN0cm8gPC0gdG9wLnByZWQuYXN0cm9bb3JkZXIodG9wLnByZWQuYXN0cm8kVmFyMSwtdG9wLnByZWQuYXN0cm8kRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFzdHJvKSA8LSBOVUxMCgoKCmBgYAoKCgpQb3NzaWJsZSBwcmVkaWN0ZWQgaW4gb3RoZXIgY2x1c3RlcnMKCmBgYHtyfQpjbHVzdHJlZShzZXUucSkKCmBgYAoKTWFrZSB0aGUgcHJlZGljdGlvbiB0YWJsZSBmb3IgaGlnaCByZXNvbHV0aW9uIFJlcyAwLjggMTIgY2x1c3RlcnMKCmBgYHtyfQojIEFJVzAwMiAxMjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuOCwgc2V1LnEkTUJPQUlXLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3MTIwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXcxMjApIDwtIE5VTEwKZGYudG9wLmFpdzEyMCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKQoKIyBBSVcwMDIgNjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuOCwgc2V1LnEkQUlXNjAucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCA8LWFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3NjAgPC0gdG9wLnByZWQuY2VsbHR5cGUuQUlXNjBbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXNjAkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wLmFpdzYwKSA8LSBOVUxMCmRmLnRvcC5haXc2MCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3NjApCgoKIyBBU1QyMyAxNjUgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuOCwgc2V1LnEkTUJPQVNUMjMucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkFTVDIzIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkFTVDIzJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykgPC0gTlVMTApkZi50b3AuQVNUMjMkSSA8LSByb3cubmFtZXMoZGYudG9wLkFTVDIzKQoKIyMjIGFkZCBpbiB0aGUgcHJlZGljdGlvbiBmcm9tIGJyYWluIGRhdGEgQmhhZHVyaSBtaWRicmFpbiBhbmQgc3RyaWF0dW0KdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjgsIHNldS5xJEJoYS5taWQuc3RyaS5wcmVkKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkJoYSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkJoYSA8LSB0b3AucHJlZC5jZWxsdHlwZS5CaGFbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQmhhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkJoYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQmhhKSA8LSBOVUxMCmRmLnRvcC5CaGEkSSA8LSByb3cubmFtZXMoZGYudG9wLkJoYSkKCiMjIHRoZXNlIGFyZSBjYWxjdWxhdGVkIGJlbG93CiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiB3aG9sZSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gZG93biBzYW1wbGVkCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC44LCBzZXUucSRCaGEpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQmhhMSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkJoYTEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhMVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5CaGEkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQmhhJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5CaGExKSA8LSBOVUxMCmRmLnRvcC5CaGExJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGExKQoKcHJlZC50YWJsZSA8LSBtZXJnZShkZi50b3AuQVNUMjMsIGRmLnRvcC5haXc2MCwgYnkgPSAnSScsIGFsbCA9IFRSVUUpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLmFpdzEyMCwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYSwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYTEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlCgpgYGAKCgoKCkxvb2sgYXQgY2x1c3RlciBtYXJrZXJzCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuMicKQ2x1c3Rlck1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc2V1LnEsIG9ubHkucG9zID0gVFJVRSkKCnRvcDUgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuPTUsIHd0ID0gYXZnX2xvZzJGQykKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IHRvcDUkZ2VuZSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQoKd3JpdGUuY3N2KENsdXN0ZXJNYXJrZXJzLCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL0dsaWExQXN0cm9jeXRlc0NsdXN0ZXJNYXJrZXJzX25ldy5jc3YiKQoKIyBmb3IgdGhlIHJlcyAwLjYgdGhlIGxhcmdlcnMgZ3JvdXBzIDAgYW5kIDEgZG9uJ3QgaGF2ZSBncmVhdCBtYXJrZXJzIGFuZCBub25lIG9mIHRoZSBtYXJrZXJzIGFyZSByZWFsbHkgdmVyeSBnb29kLiAgCiMgSSdsbCByZXJ1biB3aXRoIHJlcyAwLjIKdW5pcXVlKHNldS5xJFJOQV9zbm5fcmVzLjAuMikKCiMgc3RpbGwgbm90IG11Y2ggYmV0dGVyCgoKCmBgYAoKCgpDaGVjayBjZWxsIHR5cGUgbWFya2VycyB3aXRoIEVucmljaFIKCmBgYHtyfQoKbGlicmFyeShlbnJpY2hSKQoKc2V0RW5yaWNoclNpdGUoIkVucmljaHIiKSAjIEh1bWFuIGdlbmVzCiMgbGlzdCBvZiBhbGwgdGhlIGRhdGFiYXNlcwoKIyBsaWJhcmllcyB3aXRoIGNlbGwgdHlwZXMKCmRiIDwtIGMoJ0FsbGVuX0JyYWluX0F0bGFzX3VwJywnRGVzY2FydGVzX0NlbGxfVHlwZXNfYW5kX1Rpc3N1ZV8yMDIxJywKICAgICAgICAnQ2VsbE1hcmtlcl9BdWdtZW50ZWRfMjAyMScsJ0F6aW11dGhfQ2VsbF9UeXBlc18yMDIxJykKCiMgZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gTlVMTCkKCiNJJ2xsIHJ1biB0aGUgY2x1c3RlcnMgb25lIGF0IGEgdGltZQoKTjEuYzAgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGZpbHRlcihjbHVzdGVyID09IDUgJiBhdmdfbG9nMkZDID4gMCkKZ2VuZXMgPC0gTjEuYzAkZ2VuZQoKTjEuYzAuRXIgPC0gZW5yaWNocihnZW5lcywgZGF0YWJhc2VzID0gZGIpCnBsb3RFbnJpY2goTjEuYzAuRXJbWzFdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzAuRXJbWzJdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzAuRXJbWzNdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCnBsb3RFbnJpY2goTjEuYzAuRXJbWzRdXSwgc2hvd1Rlcm1zID0gMjAsIG51bUNoYXIgPSA0MCwgeSA9ICJDb3VudCIsIG9yZGVyQnkgPSAiUC52YWx1ZSIpCgpOMS5Fci5nZW5lcy4xIDwtIE4xLmMwLkVyW1sxXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjEKCk4xLkVyLmdlbmVzLjIgPC0gTjEuYzAuRXJbWzJdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMgoKTjEuRXIuZ2VuZXMuMyA8LSBOMS5jMC5FcltbM11dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4zCgoKTjEuRXIuZ2VuZXMuNCA8LSBOMS5jMC5FcltbNF1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy40CgoKCiMgY2x1c3RlciAwIC0gQ2VsbCB0eXBlIG1hcmtlciBsaWJyYXJ5IC0gQnJhaW4gYXN0cm9jeXRlIHRvcCBoaXQgYW5kIGVtYnJ5b25pYyBhc3Ryb2N5dGVzCiMgZ2VuZSBsaXN0IGluIHRlcm06IGJyYWluIGFzdHJvY3l0ZQlFRkVNUDE7TkZJQTtMSVgxO1BTQVA7S0lGMjFBO1MxMDBCO0NSWUFCO0RLSzMKIyBlbWJyeW8gYXN0cm9jeXRlcwlTT1gyO0JFWDE7SE1HQ1MxO1BUUFJaMTtMSVgxO1MxMDBCO0RLSzM7SVRNMkMKCiMgY2x1c3RlciAxIC0gc3RlbSBjZWxsIHBlcmljeXRlIChicmFpbiksIHN0ZWxhdGUsIGFzdHJvY3l0ZQoKIyBjbHVzdGVyIDIgLSBoeXBvdGhhbG11cywgZW5kb3RoZWxpYWwgY2VsbHMsIG1hY3JvcGhhZ2UKIyBlbmRvdGhlbGlhbCBDU1RCO1BSRUxJRDE7TVQxWDtDUklQMjtSSE9DO1RNRU0xNDE7TVQyQTtSUFMyODtDQ0RDODVCO0VJRjNJO1JCUDE7SUQxO0M0T1JGMztJRDM7UENCRDE7TVNYMTtQUElDCgojIGNsdXN0ZXIgMyAtIHNtb290aCBtdXNjbGUgY2VsbHMKCiMgY2x1c3RlciA0IC0gTksgY2VsbHMsIGZpYnJvYmxhc3RzCiMgTksgY2VsbHMgSVRHQjE7UkFCNUM7R1NUUDE7UERDRDU7RUVGMUIyO1RHT0xOMjtTRENCUDtNVDJBO0xESEE7U05SUEQyO1lXSEFRO1pORjMyNjtUTVNCMTA7Q0NEQzUwCiMgZmlicm9ibGFzdHMgCUNPTDNBMTtDQUxEMTtDT0w2QTMKCiMgY2x1c3RlciA1IC0gZW5kb3RoZWxpYWwgY2VsbHMsIE5LIGNlbGxzLCBDRDgrCgojIGNsdXN0ZXIgNiAtIHN0cm9tYWwgY2VsbHMgZXVyeXRocm9ibGFzdHMsIG5vbmUtbmV1cm9uYWwsIG9saWdvCgojIHJlcmFuIGFuZCBub3cgdGhlcmUgYXJlIG9ubHkgNSBjbHVzdGVycwojIHJlcGVhdCBjaGVja2luZwoKCmBgYAoKQ2x1c3RlciAwIC0gYXN0cm9jeXRlcwpDbHVzdGVyIDEgLSBwZXJpY3l0ZSBhc3Ryb2N5dGUgKHdlYWsgc3RpbGwpCkNsdXN0ZXIgMiAtIGVuZG90aGVsaWFsCkNsdXN0ZXIgMyAtIHNtb290aCBtdXNjbGUKQ2x1c3RlciA0IC0gTksvZmlicm9ibGFzdApDbHVzdGVyIDUgLSBlbmRvdGhlbGlhbApDbHVzdGVyIDYgLSBub24tIG5ldXJvbmFsCgoKCgoKCgpgYGB7cn0KClZsblBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gYygiQ0Q0NCIsIklUR0IxIiwiUzEwMEIiKSwgZ3JvdXAuYnkgPSAnb3JpZy5pZGVudCcpClZsblBsb3Qoc2V1LmZ0LCBmZWF0dXJlcyA9IGMoIkNENDQiLCJJVEdCMSIsIlMxMDBCIiksIGdyb3VwLmJ5ID0gJ29yaWcuaWRlbnQnKQoKYGBgCgpDaGVjayBleHByZXNzaW9uIG9mIGtub3duIG1hcmtlcnMKCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC4yJwoKZmVhdHVyZV9saXN0ID0gYygiTUtJNjciLCJTT1gyIiwiUE9VNUYxIiwiRExYMiIsIlBBWDYiLCJTT1g5IiwiSEVTMSIsIk5FUyIsIlJCRk9YMyIsIk1BUDIiLCJOQ0FNMSIsIkNEMjQiLCJHUklBMiIsIkdSSU4yQiIsIkdBQkJSMSIsIkdBRDEiLCJHQUQyIiwiR0FCUkExIiwiR0FCUkIyIiwiVEgiLCJBTERIMUExIiwiTE1YMUIiLCJOUjRBMiIsIkNPUklOIiwiQ0FMQjEiLCJLQ05KNiIsIkNYQ1I0IiwiSVRHQTYiLCJTTEMxQTMiLCJDRDQ0IiwiQVFQNCIsIlMxMDBCIiwgIlBER0ZSQSIsIk9MSUcyIiwiTUJQIiwiQ0xETjExIiwiVklNIiwiVkNBTTEiKQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVfbGlzdCkgK1JvdGF0ZWRBeGlzKCkKClBEX3BvdWxpbiA9IGMoIlRIIiwiU0xDNkEzIiwiU0xDMThBMiIsIlNPWDYiLCJORE5GIiwiU05DRyIsIkFMREgxQTEiLCJDQUxCMSIsIlRBQ1IyIiwiU0xDMTdBNiIsIlNMQzMyQTEiLCJPVFgyIiwiR1JQIiwiTFBMIiwiQ0NLIiwiVklQIikKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBQRF9wb3VsaW4sIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBQRF9wb3VsaW4pK1JvdGF0ZWRBeGlzKCkKCmVhbHJ5TmV1ciA9IGMoIkRDWCIsIk5FVVJPRDEiLCJUQlIxIikKcHJvbGlmZXJhdGlvbiA9IGMoIlBDTkEiLCJNS0k2NyIpCm5ldXJhbHN0ZW0gPSBjKCJTT1gyIiwiTkVTIiwiUEFYNiIsIk1BU0gxIikKCmZlYXR1cmVfbGlzdCA8LSBjKCJEQ1giLCJORVVST0QxIiwiVEJSMSIsIlBDTkEiLCJNS0k2NyIsIlNPWDIiLCJORVMiLCJQQVg2IiwiTUFTSDEiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0KStSb3RhdGVkQXhpcygpCgoKbWF0X25ldXJvbiA9IGMoIlJCRk9YMyIsIlNZUCIsIkRMRzQ1IiwiVkFNUDEiLCJWQU1QMiIsIlRVQkIzIiwiU1lUMSIsIkJTTiIsIkhPTUVSMSIsIlNMQzE3QTYiKSAKIyBOZXVOIGlzIEZPWDMgLSBSQkZPWDMKIyBQU0Q5NSBhbHNvIFNQLTkwIG9yIERMRzQKIyBWR0xVVDIgaXMgU0xDMTdBNgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gbWF0X25ldXJvbiwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQojIGNsdXN0ZXIgNCBhbHNvIHNob3cgbWF0dXJlIG5ldXJvbiBtYXJrZXJzCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gbWF0X25ldXJvbikrUm90YXRlZEF4aXMoKQojIGV4Y2l0YXRvcnkgbmV1cm9uIG1hcmtlcnMKZXggPSBjKCJHUklBMiIsIkdSSUExIiwiR1JJQTQiLCJHUklOMSIsIkdSSU4yQiIsIkdSSU4yQSIsIkdSSU4zQSIsIkdSSU4zIiwiR1JJUDEiLCJDQU1LMkEiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZXgsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBleCkrUm90YXRlZEF4aXMoKQojIGluaGliaXRvcnkgbmV1cm9uIG1hcmtlcnMKaW5oID0gYygiR0FEMSIsIkdBRDIiLCAiR0FUMSIsIlBWQUxCIiwiR0FCUjIiLCJHQUJSMSIsIkdCUlIxIiwiR0FCUkIyIiwiR0FCUkIxIiwiR0FCUkIzIiwiR0FCUkE2IiwiR0FCUkExIiwiR0FCUkE0IiwiVFJBSzIiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gaW5oLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gaW5oKStSb3RhdGVkQXhpcygpCiMgY2x1c3RlciA0IGlzIG1vcmUgZXhjaXRhdG9yeSB0aGFuIGluaGJpdG9yeSBidXQgbmVpdGhlciBtYXJrZXIgc2V0IGhhcyBtdWNoIGV4cHJlc3Npb24gCgojIyMgZ2xpYSBtYXJrZXJzCm1pY3JvZ2xpYSA9IGMoIlBUUFJDIiwiQUlGMSIsIkFER1JFMSIpICAjIEFER1JFMSBpcyBhIG1pY3JvZ2xpYSBtYXJrZXIgRjQvODAsIENENDUgaXMgUFRQUkMsIGdlbmUgbmFtZSBJQkExIGlzIEFJRjEKYXN0b2xnTlBDcHJvbWljcm8gPSBjKCJHRkFQIiwiUzEwMEIiLCJTTEMxQTIiLCJNQlAiLCJTT1gxMCIsIlNQUDEiLCJEQ1giLCJORVVST0QxIiwiVEJSMSIsIlBDTkEiLCJNS0k2NyIsIlBUUFJDIiwiQUlGMSIsIkFER1JFMSIpCiMgbm90ZSBHTFQxIGlzIEVBQVQyIHdoaWNoIGlzIFNMQzFBMiBnbHV0YXRtYXRlIHRyYW5zcG9ydGVyCiMgZXBpdGhlbGlhbAplcGkgPSBjKCJIRVMxIiwiSEVTNSIsIlNPWDIiLCJTT1gxMCIsIk5FUyIsIkNESDEiLCJOT1RDSDEiKSAjIGUtY2FkaGVyaW4gaXMgQ0RIMQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGFzdG9sZ05QQ3Byb21pY3JvLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gYXN0b2xnTlBDcHJvbWljcm8pK1JvdGF0ZWRBeGlzKCkKIyBjbHVzdGVyIDQgaXMgbW9yZSBleGNpdGF0b3J5IHRoYW4gaW5oYml0b3J5IGJ1dCBuZWl0aGVyIG1hcmtlciBzZXQgaGFzIG11Y2ggZXhwcmVzc2lvbiAKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGVwaSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGVwaSkrUm90YXRlZEF4aXMoKQoKIyBhbHNvIGFkZCBSYWRpYWwgZ2xpYSBtYXJrZXIgb3ZlcmxhcCB3aXRoIEdsaWEgYW5kIE5ldXJvbnMKCmZlYXR1cmVzIDwtIGMoIlBUUFJDIiwiQUlGMSIsIkFER1JFMSIsICJWSU0iLCAiVE5DIiwiUFRQUloxIiwiRkFNMTA3QSIsIkhPUFgiLCJMSUZSIiwKICAgICAgICAgICAgICAiSVRHQjUiLCJJTDZTVCIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlcywgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVzKStSb3RhdGVkQXhpcygpCgoKCgpgYGAKCk5vIFRIIGV4cHJlc3Npb24KCmNsdXN0ZXJzIDUgVklNIGhpZ2hlc3QsIFMxMDAgQiAKQ2x1c3RlciA0IApDbHVzdGVyIDMgaGFzIHNvbWUgY2VsbHMgd2l0aCBoaWdoIE9UWDIsIE5FUyBpbmRpY2F0ZXMgTlBDL1ByZWN1cnNvcnMKQ2x1c3RlciAyIGhhcyBzb21lIFNPWDIgYW5kIFBBWDYgaW5kaWNhdGVzIE5QQywgVkFNUDIgaW5kaWNhdGluZyBuZXVyb25zLCBTMTAwQiBoaWdoZXN0IGFuZCBtb3N0IC0gaW5kaWNhdGVzIGFzdHJvY3l0ZXMsIGFsc28gTUJQIGluZGljYXRlcyBvbGlnb3MKQ2x1c3RlciAxCkNsdXN0ZXIgMCAKCgpMYWJsZSB0aGUgY2x1c3RlcnMgCgpgYGB7cn0KCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuMicKCiNzZXUucSA8LSBCdWlsZENsdXN0ZXJUcmVlKHNldS5xLCByZW9yZGVyID0gVFJVRSwgcmVvcmRlci5udW1lcmljID0gVFJVRSkKdW5pcXVlKHNldS5xJFJOQV9zbm5fcmVzLjAuMikKCmNsdXN0ZXIuaWRzIDwtIGMoIkFzdHJvY3l0ZXMxIiwiQXN0cm9jeXRlczIiLCJQcmVjdXJzb3JzIiwiUkcxIiwiUkcyIiwiRW5kb3RoZWxpYWwiKQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUucSkKc2V1LnEgPC0gUmVuYW1lSWRlbnRzKHNldS5xLCBjbHVzdGVyLmlkcykKc2V1LnEkc3ViZ3JvdXBzIDwtIElkZW50cyhzZXUucSkKCiNEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjInLCBsYWJlbCA9IFRSVUUpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdzdWJncm91cHMnLCByZXBlbCA9IFRSVUUpCgojIHNvbWV0aGluZyB3ZWlyZCBpcyBnb2luZyBvbiBpbiB0aGUgb3JkZXIKCiNzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWExTGFibGVkU2V1MzAxMTAyMDIyLlJEUyIpCgoKYGBgCgoKCkNvbXBhcmUgdGhlIEFzdHJvY3l0ZSBncm91cHMgYW5kIGdldCBzb21lIG1hcmtlcnMgZm9yIHN1YiBncm91cHMKCmBgYHtyfQoKCmFzdHJvLnN1Yi5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHNldS5xLCBpZGVudC4xID0gIkFzdHJvY3l0ZXMxIiwgaWRlbnQuMiA9ICJBc3Ryb2N5dGVzMiIsIG9ubHkucG9zID0gRkFMU0UpCiN0b3A1IDwtIGFzdHJvLnN1Yi5tYXJrZXJzICU+JSB0b3BfbihuPTUsIHd0ID0gYXZnX2xvZzJGQykKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBjKCJQTENHMiIsIlBUUFJaMSIsIlNOSEcyNSIsIlZDQU4iLCJMVU0iLCJEQ04iLCJTMTAwNEEiKSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQoKYXN0cm8yIDwtIHJvd25hbWVzKGFzdHJvLnN1Yi5tYXJrZXJzICU+JSBmaWx0ZXIoYXZnX2xvZzJGQyA8IDAuMDUpKQoKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBjKCJQTENHMiIsIlBUUFJaMSIsIlNOSEcyNSIsIlZDQU4iLCJMVU0iLCJEQ04iLCJTMTAwNEEiKSkgKyBSb3RhdGVkQXhpcygpCgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gYXN0cm8yWzE6MTVdLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gYXN0cm8yWzE6MTVdKSArUm90YXRlZEF4aXMoKQoKCiMgcmFkaWFsIGdsaWEgc3VidHlwaW5nCklkZW50cyhzZXUucSkgPC0gKCdzdWJncm91cHMnKQpyZy5zdWIubWFya2VycyA8LSBGaW5kTWFya2VycyhzZXUucSwgaWRlbnQuMSA9ICJSRzEiLCBpZGVudC4yID0gIlJHMiIsIG9ubHkucG9zID0gRkFMU0UpCnRvcDUudXAgPC0gcmcuc3ViLm1hcmtlcnMgJT4lIHRvcF9uKG49MTAsIHd0ID0gYXZnX2xvZzJGQykKdG9wNS5kb3duIDwtIHJnLnN1Yi5tYXJrZXJzICU+JSB0b3BfbihuPS0xMCwgd3QgPSBhdmdfbG9nMkZDKQpmdCA8LSByb3duYW1lcyh0b3A1LnVwKQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGZ0LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZnQpICsgUm90YXRlZEF4aXMoKQoKCmZ0IDwtIHJvd25hbWVzKHRvcDUuZG93bikKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZ0KSArIFJvdGF0ZWRBeGlzKCkKCm5wYy5tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKHNldS5xLCBpZGVudC4xID0gIlByZWN1cnNvcnMiLCBpZGVudC4yID0gYygiQXN0cm9jeXRlczIiLCJBc3Ryb2N5dGVzMSIpLCBvbmx5LnBvcyA9IEZBTFNFKQp0b3AxMC5ucGMgPC0gbnBjLm1hcmtlcnMgJT4lIHRvcF9uKG49MTAsIHd0ID0gYXZnX2xvZzJGQykKbnBjLm1hcmtlcnMgPC0gbnBjLm1hcmtlcnMgJT4lIGZpbHRlcihhdmdfbG9nMkZDID4gMCkKZGltKG5wYy5tYXJrZXJzKQoKZnQgPC0gcm93bmFtZXModG9wMTAubnBjKQojIGNvbnNpZGVyIG5hbWluZyBwcmVjdXJzb3JzIGFzIGFzdHJvY3l0ZXMzCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZ0KSArIFJvdGF0ZWRBeGlzKCkKCgpucGMubWFya2Vycy5yZyA8LSBGaW5kTWFya2VycyhzZXUucSwgaWRlbnQuMSA9ICJQcmVjdXJzb3JzIiwgaWRlbnQuMiA9IGMoIlJHMiIsIlJHMSIpLCBvbmx5LnBvcyA9IFRSVUUpCnRvcDEwLm5wYyA8LSBucGMubWFya2Vycy5yZyAlPiUgdG9wX24obj0xMCwgd3QgPSBhdmdfbG9nMkZDKQoKZnQgPC0gcm93bmFtZXModG9wMTAubnBjKQojIGNvbnNpZGVyIG5hbWluZyBwcmVjdXJzb3JzIGFzIGFzdHJvY3l0ZXMzCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmdCwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZ0KSArIFJvdGF0ZWRBeGlzKCkKCiMjIHRoZXNlIGhhdmUgbW9yZSBkaWZmZXJlbmNlcyBmcm9tIFJHIHRoYW4gQXN0cm9jeXRlcwoKCmBgYAoKQWRkIHN1YnR5cGUgZ2VuZSBpZHMKCmBgYHtyfQoKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4yJykKCmNsdXN0ZXIuaWRzIDwtIGMoIkFzdHJvY3l0ZXMtUExDRzIiLCJBc3Ryb2N5dGVzLUROQyIsIkFzdHJvY3l0ZXMtSUdGQlAyIiwKICAgICAgICAgICAgICAgICAiUkcxLUNES04xQyIsIlJHMi1UWVJQMSIsIkVuZG90aGVsaWFsIikKI2NsdXN0ZXIuaWRzIDwtIGMoIkFzdHJvY3l0ZXMxIiwiQXN0cm9jeXRlczIiLCJQcmVjdXJzb3JzKEFzdHJvY3l0ZXMpIiwiUkcxIiwiUkcyIiwiRW5kb3RoZWxpYWwiKQoKbmFtZXMoY2x1c3Rlci5pZHMpIDwtIGxldmVscyhzZXUucSkKc2V1LnEgPC0gUmVuYW1lSWRlbnRzKHNldS5xLCBjbHVzdGVyLmlkcykKc2V1LnEkQ2VsbF90eXBlcyA8LSBJZGVudHMoc2V1LnEpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnQ2VsbF90eXBlcycsIHJlcGVsID0gVFJVRSkKCgpzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWExTGFibGVkU2V1MzAxMTAyMDIyLlJEUyIpCgojIGxhYmVsIHdpdGggbWFpbiBjZWxsIHR5cGUgZ3JvdXBzIAoKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4yJykKCmNsdXN0ZXIuaWRzIDwtIGMoIkFzdHJvY3l0ZXMiLCJBc3Ryb2N5dGVzIiwiQXN0cm9jeXRlcyIsCiAgICAgICAgICAgICAgICAgIlJHIiwiUkciLCJFbmRvdGhlbGlhbCIpCiNjbHVzdGVyLmlkcyA8LSBjKCJBc3Ryb2N5dGVzMSIsIkFzdHJvY3l0ZXMyIiwiUHJlY3Vyc29ycyhBc3Ryb2N5dGVzKSIsIlJHMSIsIlJHMiIsIkVuZG90aGVsaWFsIikKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1LnEpCnNldS5xIDwtIFJlbmFtZUlkZW50cyhzZXUucSwgY2x1c3Rlci5pZHMpCnNldS5xJENlbGxfVHlwZSA8LSBJZGVudHMoc2V1LnEpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnQ2VsbF9UeXBlJywgcmVwZWwgPSBUUlVFKQoKCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTFMYWJsZWRTZXUzMDExMDIwMjIuUkRTIikKCgoKCmBgYAoKCkxhYmVsIG1haW4gY2VsbCB0eXBlcyAKUmVzIDAuOAlwcmVkaWNpdG9ucwowCWFzdHJvCjEJRW5kby9SRy9Bc3RybwoyCUFzdHJvY3l0ZQozCVJHL0VuZG8vQXN0cm8KNAlBc3Rybwo1CUFzdHJvCjYJRXBpdGhlbGEsIGVuZG8sIGFzdHJvLCBSRwo3CUFzdHJvCjgJQXN0cm8KOQlBc3Ryby9SRy9FbmRvCjEwCUFzdHJvL1JHCjExCUFzdHJvL05ldXJvbnMKMTIJRW5kby9SRy4gCgoKYGBge3J9CgojRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC44JykKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC44JwoKY2x1c3Rlci5pZHMgPC0gYygiQXN0cm9jeXRlcyIsIkFzdHJvY3l0ZXMtRW5kby1SRyIsIkFzdHJvY3l0ZXMiLAogICAgICAgICAgICAgICAgICJSRy1FbmRvLUFzdHJvIiwiQXN0cm8iLCJBc3RybyIsIkVwaS1FbmRvLUFzdHJvLVJHIiwKICAgICAgICAgICAgICAgICAiQXN0cm8iLCJBc3RybyIsIkFzdHJvLVJHLUVuZG8iLCJBc3Ryby1SRyIsIkFzdHJvLU5ldXJvbnMiLCJFbmRvLVJHIikKCmNsdXN0ZXIuaWRzIDwtIGMoIkFzdHJvY3l0ZXMiLCJBc3Ryb2N5dGVzIiwiQXN0cm9jeXRlcyIsCiAgICAgICAgICAgICAgICAgIkFzdHJvY3l0ZXMiLCJBc3Ryb2N5dGVzIiwiQXN0cm9jeXRlcyIsIkVuZG90aGVsaWFsIiwKICAgICAgICAgICAgICAgICAiQXN0cm9jeXRlcyIsIkFzdHJvY3l0ZXMiLCJSYWRpYWwgR2xpYSIsIlJhZGlhbCBHbGlhIiwiQXN0cm9jeXRlcyIsIkVuZG90aGVsaWFsIikKCm5hbWVzKGNsdXN0ZXIuaWRzKSA8LSBsZXZlbHMoc2V1LnEpCnNldS5xIDwtIFJlbmFtZUlkZW50cyhzZXUucSwgY2x1c3Rlci5pZHMpCnNldS5xJENlbGxfVHlwZSA8LSBJZGVudHMoc2V1LnEpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGxhYmVsID0gVFJVRSwgZ3JvdXAuYnkgPSAnQ2VsbF9UeXBlJywgcmVwZWwgPSBUUlVFKQoKCnNhdmVSRFMoc2V1LnEsICIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL29ianMvR2xpYTFMYWJsZWRTZXUzMDExMDIwMjIuUkRTIikKCgoKYGBgCgpgYGB7cn0KdGFibGUoc2V1LnEkQ2VsbF9UeXBlKQoKYGBgCgoKClF1aWNrIGNoZWNrIHRoZSBHbGlhbDIgCgpgYGB7cn0KCiMgZXhwbG9yZSBmaWx0ZXJpbmcKc2V1IDwtIEdsaWEyCnNldQojIApWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiwgIm5Db3VudF9STkEiLCAicGVyY2VudC5tdCIpLCBuY29sID0gMykKClZsblBsb3Qoc2V1LCBwdC5zaXplID0gMC4xMCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiKSwgeS5tYXggPSAxMDAwKQpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkZlYXR1cmVfUk5BIiksIHkubWF4ID0gNTAwKQpWbG5QbG90KHNldSwgcHQuc2l6ZSA9IDAuMTAsIGZlYXR1cmVzID0gYygibkNvdW50X1JOQSIpLCB5Lm1heCA9IDIwMDApCgojIGZpbHRlciBtb3JlIGNlbGxzCgpzZXUuZnQgPC0gc3Vic2V0KHNldSwgc3Vic2V0ID0gbkZlYXR1cmVfUk5BID4gMjUwICYgbkNvdW50X1JOQSA+IDI1MCAmIG5Db3VudF9STkEgPCAxMDAwMCkgCnNldS5mdAoKVmxuUGxvdChzZXUuZnQsIHB0LnNpemUgPSAwLjEwLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIpLCB5Lm1heCA9IDIwMDApCgpgYGAKYGBge3J9CgpWbG5QbG90KHNldS5mdC5nbGlhLCBmZWF0dXJlcyA9IGMoIkNENDQiLCJTMTAwQiIsIklUR0IxIikpClZsblBsb3Qoc2V1LmZ0LCBmZWF0dXJlcyA9IGMoIkNENDQiLCJTMTAwQiIsIklUR0IxIiksIGdyb3VwLmJ5ID0gJ29yaWcuaWRlbnQnKQojIGJvdGggZ2xpYSBwb3B1bGF0aW9ucyBhcmUgc2ltaWxhcgoKCmBgYAoKTGV2ZWxzIHNlZW0gc2ltaWxhciBpbiBHbGlhMSBhbmQgR2xpYTIKClJlbW92ZSBkb3VibGV0cyBhbmQgc3RhcnQgdG8gcHJvY2VzcyBHbGlhIDIKCmBgYHtyfQoKc3VwcHJlc3NNZXNzYWdlcyhyZXF1aXJlKERvdWJsZXRGaW5kZXIpKQoKIyBmaWx0ZXJpbmcgb3V0IE1BTEFUMSBhbmQgbWl0b2Nob25kcmlhbCBnZW5lcwoKc2V1LmZ0IDwtIHNldS5mdFshZ3JlcGwoIk1BTEFUMSIsIHJvd25hbWVzKHNldS5mdCkpLCBdCnNldS5mdCA8LSBzZXUuZnRbIWdyZXBsKCJeTVQtIiwgcm93bmFtZXMoc2V1LmZ0KSksIF0KCiMgbGlrZSBpbiB0aGUgdHV0b3JpYWwgSSdtIGZvbGxvd2luZyBNQUxBVDEgaXMgdGhlIHRvcCBtb3N0IGV4cHJlc3NlZCBnZW5lLiAgVGhlIHRvcCBnZW5lcyBhcmUgYSBsb3Qgb2YgTVQgYW5kIFJpYm9zb21hbCBnZW5lcwoKc2V1LmZ0W1sicGVyY2VudC5yYiJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChzZXUuZnQsIHBhdHRlcm4gPSAiXlJQIikKCnNldS5kID0gTm9ybWFsaXplRGF0YShzZXUuZnQpCnNldS5kID0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1LmQsIHZlcmJvc2UgPSBGKQpzZXUuZCA9IFNjYWxlRGF0YShzZXUuZCwgdmFycy50by5yZWdyZXNzID0gYygibkZlYXR1cmVfUk5BIiwgInBlcmNlbnQubXQiKSwKICAgIHZlcmJvc2UgPSBGKQpzZXUuZCA9IFJ1blBDQShzZXUuZCwgdmVyYm9zZSA9IEYsIG5wY3MgPSAxNSkKc2V1LmQgPSBSdW5VTUFQKHNldS5kLCBkaW1zID0gMToxMCwgdmVyYm9zZSA9IEYpCgpuRXhwIDwtIHJvdW5kKG5jb2woc2V1LmQpICogMC4wOCkgICMgZXhwZWN0IG1vcmUgZG91YmxldHMgYmVjYXVzZSB0aGVyZSBpcyBhIGxvdCBtb3JlIGNlbGxzCnNldS5kIDwtIGRvdWJsZXRGaW5kZXJfdjMoc2V1LmQsIHBOID0gMC4yNSwgcEsgPSAwLjA5LCBuRXhwID0gbkV4cCwgUENzID0gMToxMCkKIyB0aGUgbWVtb3J5IGxpbWl0IGlzIHJlYWNoZWQgaGVyZSAtIEkgY291bGQgcnVuIG9uIGNvbXB1dGUgY2FuYWRhCiMgRm9yIG5vdyBJJ2xsIGRvd25zYW1wbGUKIyB0aGlzIHdvcmtzCgojIG5hbWUgb2YgdGhlIERGIHByZWRpY3Rpb24gY2FuIGNoYW5nZSwgc28gZXh0cmFjdCB0aGUgY29ycmVjdCBjb2x1bW4gbmFtZS4KREYubmFtZSA9IGNvbG5hbWVzKHNldS5kQG1ldGEuZGF0YSlbZ3JlcGwoIkRGLmNsYXNzaWZpY2F0aW9uIiwgY29sbmFtZXMoc2V1LmRAbWV0YS5kYXRhKSldCgoKY293cGxvdDo6cGxvdF9ncmlkKG5jb2wgPSAyLCBEaW1QbG90KHNldS5kLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikgKyBOb0F4ZXMoKSwKICAgIERpbVBsb3Qoc2V1LmQsIGdyb3VwLmJ5ID0gREYubmFtZSkgKyBOb0F4ZXMoKSkKClZsblBsb3Qoc2V1LmQsIGZlYXR1cmVzID0gIm5GZWF0dXJlX1JOQSIsIGdyb3VwLmJ5ID0gREYubmFtZSwgcHQuc2l6ZSA9IDAuMSkKCmBgYAoKCmBgYHtyfQoKc2V1LmQgPC0gc2V1LmRbLCBzZXUuZEBtZXRhLmRhdGFbLCBERi5uYW1lXT09ICJTaW5nbGV0Il0KZGltKHNldS5kKQpkaW0oc2V1LmZ0KQoKCgpgYGAKCkNsdXN0ZXIgCgpgYGB7cn0Kc2V1IDwtIE5vcm1hbGl6ZURhdGEoc2V1LmQsIG5vcm1hbGl6YXRpb24ubWV0aG9kID0gIkxvZ05vcm1hbGl6ZSIsIHNjYWxlLmZhY3RvciA9IDEwMDAwKQpzZXUgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc2V1LCBzZWxlY3Rpb24ubWV0aG9kID0gInZzdCIsIG5mZWF0dXJlcyA9IDIwMDApCnNldSA8LSBTY2FsZURhdGEoc2V1KQpzZXUgPC0gUnVuUENBKHNldSkKc2V1IDwtIFJ1blVNQVAoc2V1LCByZWR1Y3Rpb24gPSAicGNhIiwgbi5uZWlnaGJvcnMgPSAyNSwgZGltcyA9IDE6MzApCkRpbVBsb3Qoc2V1LCByZWR1Y3Rpb24gPSAidW1hcCIpCgpzZXUucSA8LSBGaW5kTmVpZ2hib3JzKHNldSwgZGltcyA9IDE6MjUsIGsucGFyYW0gPSAyNSkKc2V1LnEgPC0gRmluZENsdXN0ZXJzKHNldS5xLCByZXNvbHV0aW9uID0gYygwLDAuMDUsMC4yLDAuNCwwLjUsMC42LDAuOCkpCmxpYnJhcnkoY2x1c3RyZWUpCgpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMDUnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMScpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC4yJykKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjQnKQpEaW1QbG90KHNldS5xLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuNicpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAnUk5BX3Nubl9yZXMuMC44JykKY2x1c3RyZWUoc2V1LnEpCiMgMC40IGlzIGxpa2VseSB0aGUgYmVzdCBhbm5vdGF0ZSBzdWJncm91cHMKCgpgYGAKClByZWRpY3QgY2VsbCB0eXBlcyAKCgpgYGB7cn0KCiMgU05DQSBhbmQgY29udHJvbCBtaWRicmFpbiBvcmdhbm9pZHMgMTY1IGRheXMgaW4gY3VsdHVyZQpNQk8gPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQVNUMjNfQnJhaW5Db21tL01CT2NsdXN0ZXJzX25hbWVzMjkwNzIwMjEucmRzIikKCiMgTWlkYnJhaW4gIEFJVzAwMiAxMjAgZGF5cyBpbiBjdWx0dXJlCkFJV01CTyA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9BSVd0cmlvMTIwZGF5cy9NT2ludGVncmF0ZWRDbHVzdGVySzEyM3JlczAuOC5uYW1lc19ub3YxNl8yMDIxIikKCiMgTWlkYnJhaW4gQUlXMDAyIDYwIGRheXMgaW4gY3VsdHVyZQoKQUlXNjAgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvQUlXdHJpbzYwZGF5cy9BV0kwMDJQYXJraW5LT1BpbmtLTzYwZGF5c19sYWJlbHNfMTQwNTIwMjIucmRzIikKCgojZmlyc3QgcHJlZGljdCB3aXRoIHRoZSBNQk8gZGF0YQpJZGVudHMoTUJPKSA8LSAiY2x1c3Rlcl9sYWJlbHMiCkRlZmF1bHRBc3NheShNQk8pIDwtICJSTkEiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCnByaW50KCJmaW5kaW5nIHJlZmVyZW5jZSBhbmNob3JzIikKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IE1CTyAscXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBNQk8kY2x1c3Rlcl9sYWJlbHMpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJE1CT0FTVDIzLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdNQk9BU1QyMy5wcmVkJywgbGFiZWwgPSBUUlVFKQoKIyBzZWUgaG93IGFjY3VyYXRlIHRoZSBwcmVkaWN0aW9ucyBhcmUKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIk5vbmUiKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRNQk9BU1QyMy50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdwcmVkaWN0ZWQuaWQnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJE1CT0FTVDIzLnByZWQpCnRhYmxlKHNldS5xJE1CT0FTVDIzLnRocmVzaCkKCgogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuNCwgc2V1LnEkTUJPQVNUMjMucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCgojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBjbHVzdGVycyBkb24ndCBicmVhayB1cCBieSB0aGUgcHJlZGljdGVkIGNlbGwgdHlwZXMKCiMjIyMjIyMjIyMjIyBhbm90aGVyIHByZWRpY3Rpb25zIG5vdyB1c2luZyB0aGUgQUlXIG9yZ2Fub2lkcwoKSWRlbnRzKEFJV01CTykgPC0gInJlczA4bmFtZXMiCkRlZmF1bHRBc3NheShBSVdNQk8pIDwtICJSTkEiCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gQUlXTUJPICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IEFJV01CTyRyZXMwOG5hbWVzKQpzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRBSVcxMjAucHJlZCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0FJVzEyMC5wcmVkJywgbGFiZWwgPSBUUlVFKQogCiMjIGNoZWNrIHRoZSBwcm9wb3J0aW9uIG9mIGNlbGwgdHlwZXMgcHJlZGljdGVkIGluIGVhY2ggY2x1c3Rlcgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuNCwgc2V1LnEkTUJPQUlXLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQojIHRyeSBiYXIgY2hhcnQKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKQoKIyBzZWUgaG93IGFjY3VyYXRlIHRoZSBwcmVkaWN0aW9ucyBhcmUKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIk5vbmUiKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRBSVcxMjAudGhyZXNoIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQUlXMTIwLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkQUlXMTIwLnByZWQpCnRhYmxlKHNldS5xJEFJVzEyMC50aHJlc2gpCgojIHRoZSBwcmVkaWN0ZWQgY2VsbCB0eXBlcyBtYWtlIG1vcmUgc2Vuc2UgZnJvbSB0aGUgQUlXMDAyIG9yZ2Fub2lkCiMgbm93IHByZWRpY3Qgd2l0aCB0aGUgQUlXMDAyIDYwIGRheXMgb3JnYW5vaWQKCklkZW50cyhBSVc2MCkgPC0gImNsdXN0ZXIuaWRzIgpEZWZhdWx0QXNzYXkoQUlXNjApIDwtICJSTkEiCgphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gQUlXNjAsIHF1ZXJ5ID0gc2V1LnEsIGRpbXMgPSAxOjI1KQpwcmludCgiZ2V0dGluZyBwcmVkaWN0aW9ucyIpCnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSBhbmNob3JzLCByZWZkYXRhID0gQUlXNjAkY2x1c3Rlci5pZHMpIApzZXUucSA8LSBBZGRNZXRhRGF0YShzZXUucSwgbWV0YWRhdGEgPSBwcmVkaWN0aW9ucykKcHJpbnQodGFibGUoc2V1LnEkcHJlZGljdGVkLmlkKSkKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKIyBhZGQgbmV3IGRhdGFzbG90IGZvciBNQk8gcHJlZGljdGVkIElEIHRvIG1ha2UgdGhlIG5leHQgcHJlZGljdGlvbgpzZXUucSRBSVc2MC5wcmVkIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQUlXNjAucHJlZCcsIGxhYmVsID0gVFJVRSkKIAojIyBjaGVjayB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHByZWRpY3RlZCBpbiBlYWNoIGNsdXN0ZXIKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjQsIHNldS5xJEFJVzYwLnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQoKIyB0cnkgYmFyIGNoYXJ0CmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikKCiMgc2VlIGhvdyBhY2N1cmF0ZSB0aGUgcHJlZGljdGlvbnMgYXJlCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjk1LCBzZXUucSRwcmVkaWN0ZWQuaWQsICJOb25lIikKCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQUlXNjAudGhyZXNoIDwtIElkZW50cyhzZXUucSkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQUlXNjAudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRBSVc2MC5wcmVkKQp0YWJsZShzZXUucSRBSVc2MC50aHJlc2gpCgoKCiMgbW9zdCBvZiB0aGUgY2VsbHMgYXJlIHByZWRpY3RlZCBhcyBOUENzIGluIG1hbnkgcG9wdWxhdGlvbnMKCgoKYGBgCgpgYGB7cn0KIyBzYXZlIHdpdGggcHJlZGljdGlvbnMgc28gZmFyCiNzYXZlUkRTKHNldS5xLCAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzL0dsaWEyTGFibGVkU2V1MDMxMDIwMjIuUkRTIikKCnNldS5xIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMkxhYmxlZFNldTAzMTAyMDIyLlJEUyIpCgoKYGBgCgoKU2VlIGhvdyBtYW55IGNlbGxzIGFyZSBwcmVkaWN0ZWQgYXMgYXN0cm9jeXRlcyB3aXRoIHRoZSB0aHJlc2hvbGQKCmBgYHtyfQoKREFzdWJ0eXBlcyA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9NYWNvc2tvX0RhdGEvREFzdWJncm91cHNfcHJvY2Vzc2VkLlJkcyIpCgpJZGVudHMoYXN0cm8ucmVmKSA8LSAiQ2VsbF9TdWJ0eXBlIgpEZWZhdWx0QXNzYXkoYXN0cm8ucmVmKSA8LSAiUk5BIgoKIyBmaW5kIHRoZSByZWZlcmVuY2UgYW5jaG9ycwpwcmludCgiZmluZGluZyByZWZlcmVuY2UgYW5jaG9ycyIpCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBEQXN1YnR5cGVzICxxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyMCkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IGFzdHJvLnJlZiRDZWxsX1N1YnR5cGUsIGsud2VpZ2h0ID0gMTApCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJGFzdHJvLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdhc3Ryby5wcmVkJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRhc3Ryby5wcmVkKQoKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRhc3Ryby5wcmVkLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ2FzdHJvLnByZWQudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRhc3Ryby5wcmVkLnRocmVzaCkKCgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkYXN0cm8ucHJlZC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuYXN0cm8gPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5hc3RybyA8LSB0b3AucHJlZC5hc3Ryb1tvcmRlcih0b3AucHJlZC5hc3RybyRWYXIxLC10b3AucHJlZC5hc3RybyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYXN0cm8pIDwtIE5VTEwKCgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkYXN0cm8ucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5hc3RybyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFzdHJvIDwtIHRvcC5wcmVkLmFzdHJvW29yZGVyKHRvcC5wcmVkLmFzdHJvJFZhcjEsLXRvcC5wcmVkLmFzdHJvJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5hc3RybykgPC0gTlVMTAoKIyBhIGxvdCBvZiB0aGVzZSBjZWxscyBhcmUgYWxzbyBnZXR0aW5nIGxhYmVsbGVkIGFzIGFzdHJvY3l0ZXMKCgpgYGAKCkRvIHRoZXNlIGdldCBsYWJlbGxlZCBhcyBEQSBuZXVyb25zIHRvbz8/PwoKYGBge3J9CnNldS5xIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMkxhYmxlZFNldTAzMTAyMDIyLlJEUyIpCgpgYGAKCgoKYGBge3J9CgpEQXN1YnR5cGVzIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL01hY29za29fRGF0YS9EQXN1Ymdyb3Vwc19wcm9jZXNzZWQuUmRzIikKSWRlbnRzKERBc3VidHlwZXMpIDwtICJDZWxsX1N1YnR5cGUiCmRhLnJlZiA8LSBzdWJzZXQoREFzdWJ0eXBlcywgZG93bnNhbXBsZSA9IDUwMCkKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKcHJpbnQoImZpbmRpbmcgcmVmZXJlbmNlIGFuY2hvcnMiKQphbmNob3JzIDwtIEZpbmRUcmFuc2ZlckFuY2hvcnMocmVmZXJlbmNlID0gZGEucmVmLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyMCkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IGRhLnJlZiRDZWxsX1N1YnR5cGUsIGsud2VpZ2h0ID0gMTApCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwojIGFkZCBuZXcgZGF0YXNsb3QgZm9yIE1CTyBwcmVkaWN0ZWQgSUQgdG8gbWFrZSB0aGUgbmV4dCBwcmVkaWN0aW9uCnNldS5xJGRhLnByZWQgPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdkYS5wcmVkJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRkYS5wcmVkKQoKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOTUsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRkYS5wcmVkLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ2RhLnByZWQudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRkYS5wcmVkLnRocmVzaCkKCgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkZGEucHJlZC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuYXN0cm8gPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5hc3RybyA8LSB0b3AucHJlZC5hc3Ryb1tvcmRlcih0b3AucHJlZC5hc3RybyRWYXIxLC10b3AucHJlZC5kYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYXN0cm8pIDwtIE5VTEwKCgp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkYXN0cm8ucHJlZCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5hc3RybyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFzdHJvIDwtIHRvcC5wcmVkLmFzdHJvW29yZGVyKHRvcC5wcmVkLmFzdHJvJFZhcjEsLXRvcC5wcmVkLmFzdHJvJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5hc3RybykgPC0gTlVMTAoKCiMgYWZ0ZXIgdGhyZXNob2xkaW5nIHZlcnkgZmV3IGNlbGxzIGFyZSBwcmVkaWN0ZWQgYXMgbmV1cm9ucwoKCgpgYGAKCntyZWRpY3RlIHdpdGggdGhlIGJyYWluIHNjUk5Bc2VxCgpgYGB7cn0KCnBhdGh3YXkgPC0gIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy8iCnNldS5xIDwtIHJlYWRSRFMocGFzdGUocGF0aHdheSwiR2xpYTJMYWJsZWRTZXUwMzEwMjAyMi5SRFMiLHNlcCA9ICIiKSkKCiMgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCgpzZXUuciA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QdWJsaWNEYXRhL0JoYWR1cmlfd2hvbGVCcmFpbi9CaGFkdXJpX21pZGJyYWluX3N0cmlhdHVtLlJEUyIpCgpJZGVudHMoc2V1LnIpIDwtICJjZWxsX2NsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX2NsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRCaGEubWlkLnN0cmkucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJEJoYS5taWQuc3RyaS5wcmVkKSkKCgpzZXUucSRwcmVkaWN0ZWQuaWQgPC0gaWZlbHNlKHNldS5xJHByZWRpY3Rpb24uc2NvcmUubWF4ID4gMC45NSwgc2V1LnEkcHJlZGljdGVkLmlkLCAibm9uZSIpCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQmhhLm1pZC5wcmVkLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5taWQucHJlZC50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJEJoYS5taWQucHJlZC50aHJlc2gpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5taWQucHJlZC50aHJlc2gnKQoKCiMgcmVhZCBpbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQKIyB3aG9sZSBicmFpbiBCaGFkdXJpIGRvd24gc2FtcGxlZApzZXUuciA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QdWJsaWNEYXRhL0JoYWR1cmlfd2hvbGVCcmFpbi9CaGFkdXJpX2Rvd25zYW1wbGUuUkRTIikKCklkZW50cyhzZXUucikgPC0gImNlbGxfY2x1c3RlciIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfY2x1c3RlcikKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5wcmVkIDwtIElkZW50cyhzZXUucSkKcHJpbnQodGFibGUoc2V1LnEkQmhhLnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEucHJlZCcpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycpCgpzZXUucSRwcmVkaWN0ZWQuaWQgPC0gaWZlbHNlKHNldS5xJHByZWRpY3Rpb24uc2NvcmUubWF4ID4gMC45NSwgc2V1LnEkcHJlZGljdGVkLmlkLCAibm9uZSIpCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQmhhLm1pZC5wcmVkLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5wcmVkLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkQmhhLm1pZC5wcmVkLnRocmVzaCkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQmhhLnByZWQudGhyZXNoJykKCgpgYGAKCgoKQ29tcGFyZSBwcmVkaWN0aW9ucyAtIG1ha2UgYSBwcmVkaWN0aW9ucyB0YWJsZQoKYGBge3J9CgojIEFJVzAwMiAxMjAgZGF5cyBwcmVkaWN0aW9ucyAtIHRha2UgdGhlIHRocmVzaG9sZGVkIG9wdGlvbnMKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJEFJVzEyMC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AuYWl3MTIwIDwtIHRvcC5wcmVkLmNlbGx0eXBlLkFJVzEyMFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVcxMjAkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQUlXMTIwJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5haXcxMjApIDwtIE5VTEwKZGYudG9wLmFpdzEyMCRJIDwtIHJvdy5uYW1lcyhkZi50b3AuYWl3MTIwKQoKIyBBSVcwMDIgNjAgZGF5cyBwcmVkaWN0aW9ucwp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkQUlXNjAudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkFJVzYwIDwtYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5haXc2MCA8LSB0b3AucHJlZC5jZWxsdHlwZS5BSVc2MFtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BSVc2MCRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BSVc2MCRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuYWl3NjApIDwtIE5VTEwKZGYudG9wLmFpdzYwJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5haXc2MCkKCgojIEFTVDIzIDE2NSBkYXlzIHByZWRpY3Rpb25zCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRNQk9BU1QyMy50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQVNUMjMgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcC5BU1QyMyA8LSB0b3AucHJlZC5jZWxsdHlwZS5BU1QyM1tvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRWYXIxLC10b3AucHJlZC5jZWxsdHlwZS5BU1QyMyRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQVNUMjMpIDwtIE5VTEwKZGYudG9wLkFTVDIzJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5BU1QyMykKCiMgYWRkIHRoZSB0aHJlc2hvbGQgQXN0cm8gcHJlZGljdGlvbnMgCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRhc3Ryby5wcmVkLnRocmVzaCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5hc3RybyA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmFzdHJvIDwtIHRvcC5wcmVkLmNlbGx0eXBlLmFzdHJvW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLmFzdHJvJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLmFzdHJvJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5hc3RybykgPC0gTlVMTApkZi50b3AuYXN0cm8kSSA8LSByb3cubmFtZXMoZGYudG9wLmFzdHJvKQoKIyBhZGQgdGhlIG5ldXJvbnMgcHJlZGljdGlvbnMgCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRkYS5wcmVkLnRocmVzaCkpCnQubGFibGVzJEZyZXEgPC0gYXMuZG91YmxlKHQubGFibGVzJEZyZXEpCmdncGxvdCh0LmxhYmxlcywgYWVzKHkgPSBGcmVxLCB4ID0gVmFyMSwgZmlsbCA9IFZhcjIpKSArIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIiwgc3RhdD0gImlkZW50aXR5IikgKyBSb3RhdGVkQXhpcygpIAp0b3AucHJlZC5jZWxsdHlwZS5kYSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLmRhIDwtIHRvcC5wcmVkLmNlbGx0eXBlLmRhW29yZGVyKHRvcC5wcmVkLmNlbGx0eXBlLmRhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLmRhJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5kYSkgPC0gTlVMTApkZi50b3AuZGEkSSA8LSByb3cubmFtZXMoZGYudG9wLmRhKQoKCiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRCaGEubWlkLnByZWQudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlLkJoYSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkJoYSA8LSB0b3AucHJlZC5jZWxsdHlwZS5CaGFbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUuQmhhJFZhcjEsLXRvcC5wcmVkLmNlbGx0eXBlLkJoYSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3AuQmhhKSA8LSBOVUxMCmRmLnRvcC5CaGEkSSA8LSByb3cubmFtZXMoZGYudG9wLkJoYSkKCiMjIHRoZXNlIGFyZSBjYWxjdWxhdGVkIGJlbG93CiMjIyBhZGQgaW4gdGhlIHByZWRpY3Rpb24gZnJvbSBicmFpbiB3aG9sZSBicmFpbiBkYXRhIEJoYWR1cmkgbWlkYnJhaW4gZG93biBzYW1wbGVkCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRCaGEucHJlZC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUuQmhhMSA8LSBhcy5kYXRhLmZyYW1lKHQubGFibGVzICAlPiUgZ3JvdXBfYnkoVmFyMSkgICU+JSB0b3BfbigyLCBGcmVxKSkKZGYudG9wLkJoYTEgPC0gdG9wLnByZWQuY2VsbHR5cGUuQmhhMVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZS5CaGEkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUuQmhhJEZyZXEpLF0Kcm93Lm5hbWVzKGRmLnRvcC5CaGExKSA8LSBOVUxMCmRmLnRvcC5CaGExJEkgPC0gcm93Lm5hbWVzKGRmLnRvcC5CaGExKQoKcHJlZC50YWJsZSA8LSBtZXJnZShkZi50b3AuQVNUMjMsIGRmLnRvcC5haXc2MCwgYnkgPSAnSScsIGFsbCA9IFRSVUUpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLmFpdzEyMCwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYSwgYnkgPSAnSScpCnByZWQudGFibGUgPC0gbWVyZ2UocHJlZC50YWJsZSwgZGYudG9wLkJoYTEsIGJ5ID0gJ0knKQpwcmVkLnRhYmxlCgoKCmBgYAoKUHJlZGljdGVkIGNsdXN0ZXIgYW5ub3RhdGlvbnMKMAlVbmtub3duLyBOUEMKMQlSRwoyCWFzdHJvCjMJUkcKNAluZXVyb25zCjUJUkcKClByZWRpY3QgZnJvbSBkZXZlbG9waW5nIGNvcnRleCAKCmBgYHtyfQoKcGF0aHdheSA8LSAiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUGhlbm9JRC9zY1JOQXNlcVNvcnRlZC9vYmpzLyIKc2V1LnEgPC0gcmVhZFJEUyhwYXN0ZShwYXRod2F5LCJHbGlhMkxhYmxlZFNldTAzMTAyMDIyLlJEUyIsc2VwID0gIiIpKQoKc2V1LnIgPC0gcmVhZFJEUygiL1VzZXJzL3JoYWxlbmF0aG9tYXMvRG9jdW1lbnRzL0RhdGEvc2NSTkFzZXEvUHVibGljRGF0YS9Ob3dha293c2tpX2Rldl9jb3J0ZXh0LlJEUyIpCmNvbG5hbWVzKHNldS5yQG1ldGEuZGF0YSkKCklkZW50cyhzZXUucikgPC0gIldHQ05BY2x1c3RlciIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJFdHQ05BY2x1c3RlcikKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJGRldi5jb3J0ZXgucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJGRldi5jb3J0ZXgucHJlZCkpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ2Rldi5jb3J0ZXgucHJlZCcpCgoKIyBhZGQgdGhlIHRocmVzaG9sZCAKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuOCwgc2V1LnEkcHJlZGljdGVkLmlkLCAibm9uZSIpCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkZGV2LmNvcnRleC5wcmVkLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ2Rldi5jb3J0ZXgucHJlZC50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJGRldi5jb3J0ZXgucHJlZC50aHJlc2gpCgoKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJGRldi5jb3J0ZXgucHJlZC50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oMiwgRnJlcSkpCmRmLnRvcCA8LSB0b3AucHJlZC5jZWxsdHlwZVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3ApIDwtIE5VTEwKZGYudG9wJEkgPC0gcm93Lm5hbWVzKGRmLnRvcCkKCgojIGFsbW9zdCBldmVyeXRoaW5nIGlzIHByZWRpY3RlZCBhcyAnbm9uZScgd2hlbiB0aGVzaG9sZCBpcyAuOTUgY2hlY2sgcHJlZGljdGlvbnMgd2l0aG91dCB0aHJlc2hvbGQgYW5kIHRoZXJlIGFyZSBtYW55IAojIHJ1biBhZ2FpbiB3aXRoIGxvd2VyIHRocmVzaG9sZCAwLjgKCnQubGFibGVzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkUk5BX3Nubl9yZXMuMC4yLCBzZXUucSRkZXYuY29ydGV4LnByZWQpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oNCwgRnJlcSkpCmRmLnRvcCA8LSB0b3AucHJlZC5jZWxsdHlwZVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3ApIDwtIE5VTEwKZGYudG9wJEkgPC0gcm93Lm5hbWVzKGRmLnRvcCkKCmRmLnRvcC5mdCA8LSBkZi50b3AgJT4lIGZpbHRlcihGcmVxID4gMCkKCgpgYGAKCgpUaGUgZGV2IGNvcnRleCBkb2Vzbid0IHByZWRpY3QgR2xpYSAyIHdlbGwKVHJ5IHRoZSBkZXZlbG9waW5nIGZvcmVicmFpbgoKYGBge3J9CnNldS5yIDwtIHJlYWRSRFMoIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1B1YmxpY0RhdGEvS2Fyb2xpbnNraV9EZXZGb3JlYnJhaW5fZG93bnNhbXBsZV9MZXZlbDEuUkRTIikKY29sbmFtZXMoc2V1LnJAbWV0YS5kYXRhKQpEZWZhdWx0QXNzYXkoc2V1LnIpIDwtICdSTkEnCgojIGNsdXN0ZXJzIGhhcyBzdWJncm91cHMKIyBsZXZlbHMgYXJlIG1haW4gY2VsbCBncm91cHMgCklkZW50cyhzZXUucikgPC0gIkxldmVsMSIKCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRMZXZlbDEpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRmYi5wcmVkIDwtIElkZW50cyhzZXUucSkKcHJpbnQodGFibGUoc2V1LnEkZmIucHJlZCkpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ2ZiLnByZWQnKQoKCiMgYWRkIHRoZSB0aHJlc2hvbGQgCnNldS5xJHByZWRpY3RlZC5pZCA8LSBpZmVsc2Uoc2V1LnEkcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjgwLCBzZXUucSRwcmVkaWN0ZWQuaWQsICJub25lIikKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRmYi50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdmYi50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJGZiLnRocmVzaCkKCiMgbWFrZSB0aGUgdGFibGVzIAp0LmxhYmxlcyA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKHNldS5xJFJOQV9zbm5fcmVzLjAuMiwgc2V1LnEkZmIudGhyZXNoKSkKdC5sYWJsZXMkRnJlcSA8LSBhcy5kb3VibGUodC5sYWJsZXMkRnJlcSkKZ2dwbG90KHQubGFibGVzLCBhZXMoeSA9IEZyZXEsIHggPSBWYXIxLCBmaWxsID0gVmFyMikpICsgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siLCBzdGF0PSAiaWRlbnRpdHkiKSArIFJvdGF0ZWRBeGlzKCkgCnRvcC5wcmVkLmNlbGx0eXBlIDwtIGFzLmRhdGEuZnJhbWUodC5sYWJsZXMgICU+JSBncm91cF9ieShWYXIxKSAgJT4lIHRvcF9uKDIsIEZyZXEpKQpkZi50b3AgPC0gdG9wLnByZWQuY2VsbHR5cGVbb3JkZXIodG9wLnByZWQuY2VsbHR5cGUkVmFyMSwtdG9wLnByZWQuY2VsbHR5cGUkRnJlcSksXQpyb3cubmFtZXMoZGYudG9wKSA8LSBOVUxMCmRmLnRvcCRJIDwtIHJvdy5uYW1lcyhkZi50b3ApCgojVkxNQyBpcyB2YXNjdWxhciBhbmQgbGVwdG9tZW5pbmdlcwoKIyBhbG1vc3QgZXZlcnl0aGluZyBpcyBwcmVkaWN0ZWQgYXMgJ25vbmUnIHdoZW4gdGhlc2hvbGQgaXMgLjk1IAojIHJ1biBhZ2FpbiB3aXRoIGxvd2VyIHRocmVzaG9sZCAwLjggYW5kIG1hbnkgYXJlIHByZWRpY3RlZCBhcyBSRwoKIyB0cnkgcHJlZGljdGluZyB3aXRoIHRoZSBjbHVzdGVyIGxhYmVscyAKIyBsYXRlciBJIGNhbiBzdWJzZXQgY2VsbCB0eXBlcyBmb3IgdGhlIHJlZmVyZW5jZSBkYXRhCgpJZGVudHMoc2V1LnIpIDwtICJDbHVzdGVycyIKCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRDbHVzdGVycykKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJGZiLnN1Yi5wcmVkIDwtIElkZW50cyhzZXUucSkKcHJpbnQodGFibGUoc2V1LnEkZmIuc3ViLnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdmYi5zdWIucHJlZCcpCgoKIyBhZGQgdGhlIHRocmVzaG9sZCAKc2V1LnEkcHJlZGljdGVkLmlkIDwtIGlmZWxzZShzZXUucSRwcmVkaWN0aW9uLnNjb3JlLm1heCA+IDAuODAsIHNldS5xJHByZWRpY3RlZC5pZCwgIm5vbmUiKQpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJGZiLnN1Yi50aHJlc2ggPC0gSWRlbnRzKHNldS5xKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdmYi5zdWIudGhyZXNoJywgbGFiZWwgPSBUUlVFKQp0YWJsZShzZXUucSRmYi5zdWIudGhyZXNoKQoKdC5sYWJsZXMgPC0gYXMuZGF0YS5mcmFtZSh0YWJsZShzZXUucSRSTkFfc25uX3Jlcy4wLjIsIHNldS5xJGZiLnN1Yi50aHJlc2gpKQp0LmxhYmxlcyRGcmVxIDwtIGFzLmRvdWJsZSh0LmxhYmxlcyRGcmVxKQpnZ3Bsb3QodC5sYWJsZXMsIGFlcyh5ID0gRnJlcSwgeCA9IFZhcjEsIGZpbGwgPSBWYXIyKSkgKyBnZW9tX2Jhcihwb3NpdGlvbiA9ICJzdGFjayIsIHN0YXQ9ICJpZGVudGl0eSIpICsgUm90YXRlZEF4aXMoKSAKdG9wLnByZWQuY2VsbHR5cGUgPC0gYXMuZGF0YS5mcmFtZSh0LmxhYmxlcyAgJT4lIGdyb3VwX2J5KFZhcjEpICAlPiUgdG9wX24oNCwgRnJlcSkpCmRmLnRvcCA8LSB0b3AucHJlZC5jZWxsdHlwZVtvcmRlcih0b3AucHJlZC5jZWxsdHlwZSRWYXIxLC10b3AucHJlZC5jZWxsdHlwZSRGcmVxKSxdCnJvdy5uYW1lcyhkZi50b3ApIDwtIE5VTEwKZGYudG9wJEkgPC0gcm93Lm5hbWVzKGRmLnRvcCkKCmRmLnRvcC5mdCA8LSBkZi50b3AgJT4lIGZpbHRlcihGcmVxID4gMCkKCmBgYAoKUHJlZGljdGlvbnMgd2l0aCB0aHJlc2hvbGRzOgoKMCAtIFJHCjEgLSBSRwoyIC0gbm9uZQozIC0gUkcKNCAtIE5ldXJhbCBwcmVjdXJzb3IKNSAtIFZMTUMgKHZhc2N1bGFyIGFuZCBsZXB0b21lbmluZ2VzKQoKTG9vayBhdCBnZW5lIGxpc3RzIHdpdGgga25vd24gbWFya2VycwoKYGBge3J9CgpJZGVudHMoc2V1LnEpIDwtICdSTkFfc25uX3Jlcy4wLjInCgojIG1hbnkgY2VsbCB0eXBlcyBsaXN0CmZlYXR1cmVfbGlzdCA9IGMoIk1LSTY3IiwiU09YMiIsIlBPVTVGMSIsIkRMWDIiLCJQQVg2IiwiU09YOSIsIkhFUzEiLCJORVMiLCJSQkZPWDMiLCJNQVAyIiwiTkNBTTEiLCJDRDI0IiwiR1JJQTIiLCJHUklOMkIiLCJHQUJCUjEiLCJHQUQxIiwiR0FEMiIsIkdBQlJBMSIsIkdBQlJCMiIsIlRIIiwiQUxESDFBMSIsIkxNWDFCIiwiTlI0QTIiLCJDT1JJTiIsIkNBTEIxIiwiS0NOSjYiLCJDWENSNCIsIklUR0E2IiwiU0xDMUEzIiwiQ0Q0NCIsIkFRUDQiLCJTMTAwQiIsICJQREdGUkEiLCJPTElHMiIsIk1CUCIsIkNMRE4xMSIsIlZJTSIsIlZDQU0xIikKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlX2xpc3QpICtSb3RhdGVkQXhpcygpCgojIERvcGFtaW5lcmdpYyBtYXJrZXJzClBEX3BvdWxpbiA9IGMoIlRIIiwiU0xDNkEzIiwiU0xDMThBMiIsIlNPWDYiLCJORE5GIiwiU05DRyIsIkFMREgxQTEiLCJDQUxCMSIsIlRBQ1IyIiwiU0xDMTdBNiIsIlNMQzMyQTEiLCJPVFgyIiwiR1JQIiwiTFBMIiwiQ0NLIiwiVklQIikKCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBQRF9wb3VsaW4sIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBQRF9wb3VsaW4pK1JvdGF0ZWRBeGlzKCkKCmVhbHJ5TmV1ciA9IGMoIkRDWCIsIk5FVVJPRDEiLCJUQlIxIikKcHJvbGlmZXJhdGlvbiA9IGMoIlBDTkEiLCJNS0k2NyIpCm5ldXJhbHN0ZW0gPSBjKCJTT1gyIiwiTkVTIiwiUEFYNiIsIk1BU0gxIikKCmZlYXR1cmVfbGlzdCA8LSBjKCJEQ1giLCJORVVST0QxIiwiVEJSMSIsIlBDTkEiLCJNS0k2NyIsIlNPWDIiLCJORVMiLCJQQVg2IiwiTUFTSDEiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0LCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gZmVhdHVyZV9saXN0KStSb3RhdGVkQXhpcygpCgoKbWF0X25ldXJvbiA9IGMoIlJCRk9YMyIsIlNZUCIsIkRMRzQ1IiwiVkFNUDEiLCJWQU1QMiIsIlRVQkIzIiwiU1lUMSIsIkJTTiIsIkhPTUVSMSIsIlNMQzE3QTYiKSAKIyBOZXVOIGlzIEZPWDMgLSBSQkZPWDMKIyBQU0Q5NSBhbHNvIFNQLTkwIG9yIERMRzQKIyBWR0xVVDIgaXMgU0xDMTdBNgpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gbWF0X25ldXJvbiwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQojIGNsdXN0ZXIgNCBhbHNvIHNob3cgbWF0dXJlIG5ldXJvbiBtYXJrZXJzCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gbWF0X25ldXJvbikrUm90YXRlZEF4aXMoKQojIGV4Y2l0YXRvcnkgbmV1cm9uIG1hcmtlcnMKZXggPSBjKCJHUklBMiIsIkdSSUExIiwiR1JJQTQiLCJHUklOMSIsIkdSSU4yQiIsIkdSSU4yQSIsIkdSSU4zQSIsIkdSSU4zIiwiR1JJUDEiLCJDQU1LMkEiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gZXgsIHNpemU9MywgYW5nbGUgPTkwLCBncm91cC5iYXIuaGVpZ2h0ID0gMC4wMikKRG90UGxvdChzZXUucSwgZmVhdHVyZXMgPSBleCkrUm90YXRlZEF4aXMoKQojIGluaGliaXRvcnkgbmV1cm9uIG1hcmtlcnMKaW5oID0gYygiR0FEMSIsIkdBRDIiLCAiR0FUMSIsIlBWQUxCIiwiR0FCUjIiLCJHQUJSMSIsIkdCUlIxIiwiR0FCUkIyIiwiR0FCUkIxIiwiR0FCUkIzIiwiR0FCUkE2IiwiR0FCUkExIiwiR0FCUkE0IiwiVFJBSzIiKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gaW5oLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gaW5oKStSb3RhdGVkQXhpcygpCiMgY2x1c3RlciA0IGlzIG1vcmUgZXhjaXRhdG9yeSB0aGFuIGluaGJpdG9yeSBidXQgbmVpdGhlciBtYXJrZXIgc2V0IGhhcyBtdWNoIGV4cHJlc3Npb24gCgojIyMgZ2xpYSBtYXJrZXJzCm1pY3JvZ2xpYSA9IGMoIlBUUFJDIiwiQUlGMSIsIkFER1JFMSIpICAjIEFER1JFMSBpcyBhIG1pY3JvZ2xpYSBtYXJrZXIgRjQvODAsIENENDUgaXMgUFRQUkMsIGdlbmUgbmFtZSBJQkExIGlzIEFJRjEKYXN0b2xnTlBDcHJvbWljcm8gPSBjKCJHRkFQIiwiUzEwMEIiLCJTTEMxQTIiLCJNQlAiLCJTT1gxMCIsIlNQUDEiLCJEQ1giLCJORVVST0QxIiwiVEJSMSIsIlBDTkEiLCJNS0k2NyIsIlBUUFJDIiwiQUlGMSIsIkFER1JFMSIpCiMgbm90ZSBHTFQxIGlzIEVBQVQyIHdoaWNoIGlzIFNMQzFBMiBnbHV0YXRtYXRlIHRyYW5zcG9ydGVyCiMgZXBpdGhlbGlhbAplcGkgPSBjKCJIRVMxIiwiSEVTNSIsIlNPWDIiLCJTT1gxMCIsIk5FUyIsIkNESDEiLCJOT1RDSDEiKSAjIGUtY2FkaGVyaW4gaXMgQ0RIMQoKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGFzdG9sZ05QQ3Byb21pY3JvLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gYXN0b2xnTlBDcHJvbWljcm8pK1JvdGF0ZWRBeGlzKCkKIyBjbHVzdGVyIDQgaXMgbW9yZSBleGNpdGF0b3J5IHRoYW4gaW5oYml0b3J5IGJ1dCBuZWl0aGVyIG1hcmtlciBzZXQgaGFzIG11Y2ggZXhwcmVzc2lvbiAKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IGVwaSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGVwaSkrUm90YXRlZEF4aXMoKQoKIyBhbHNvIGFkZCBSYWRpYWwgZ2xpYSBtYXJrZXIgb3ZlcmxhcCB3aXRoIEdsaWEgYW5kIE5ldXJvbnMKCmZlYXR1cmVzIDwtIGMoIlBUUFJDIiwiQUlGMSIsIkFER1JFMSIsICJWSU0iLCAiVE5DIiwiUFRQUloxIiwiRkFNMTA3QSIsIkhPUFgiLCJMSUZSIiwKICAgICAgICAgICAgICAiSVRHQjUiLCJJTDZTVCIpCkRvSGVhdG1hcChzZXUucSwgZmVhdHVyZXMgPSBmZWF0dXJlcywgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQpEb3RQbG90KHNldS5xLCBmZWF0dXJlcyA9IGZlYXR1cmVzKStSb3RhdGVkQXhpcygpCgojIHJhZGlhbCBnbGlhIG1hcmtlcnMKcmcgPC0gYygiVklNIiwiTkVTIiwiUEFYNiIsIkhFUzEiLCJFQUFUMSIsIk5DQUQxIiwiU09YMiIsIkZBQlA3IikKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IHJnLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCkRvdFBsb3Qoc2V1LnEsIGZlYXR1cmVzID0gcmcpK1JvdGF0ZWRBeGlzKCkKCiMgTlBDIGFuZCByYWRpYWwgZ2xpYSBhcmUgdmVyeSBzaW1pbGFyCgpgYGAKTWFya2VyIGV4cHJlc3Npb24gcHJlZGljdGlvbnMKQ2x1c3RlciAwIC0gdW5rbm93bgpDbHVzdGVyIDEgLSBSRwpDbHVzdGVyIDIgLSB1bmtub3duCkNsdXN0ZXIgMyAtIFJHCmNsdXN0ZXIgNCAtIGltbWF0dXJlIG5ldXJvbnMKQ2x1c3RlciA1IC0gUkcsIG9wYwoKCkNoZWNrIHRoZSBsZXZlbHMgb2YgUk5BIGluIGVhY2ggY2x1c3RlciAKCmBgYHtyfQpWbG5QbG90KHNldS5xLCBmZWF0dXJlcyA9ICJuRmVhdHVyZV9STkEiKQoKYGBgCkNsdXN0ZXIgMCBhbmQgMiBoYXZlIGZld2VyIHNlcXVlbmNlcyB0aGFuIG90aGVyIGdyb3VwcyBhbmQgdGh1cyBubyBtYXJrZXJzClBvc3NpYmx5IHJlbW92ZSB0aGVzZSBpcyB0aGV5IGRvbid0IGNvbWUgdXAgd2l0aCBzb21lIG1hcmtlcnMKCkZpbmQgY2x1c3RlciBtYXJrZXJzCgpgYGB7cn0KSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC4yJwpDbHVzdGVyTWFya2VycyA8LSBGaW5kQWxsTWFya2VycyhzZXUucSwgb25seS5wb3MgPSBUUlVFKQoKdG9wNSA8LSBDbHVzdGVyTWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKG49NSwgd3QgPSBhdmdfbG9nMkZDKQpEb0hlYXRtYXAoc2V1LnEsIGZlYXR1cmVzID0gdG9wNSRnZW5lLCBzaXplPTMsIGFuZ2xlID05MCwgZ3JvdXAuYmFyLmhlaWdodCA9IDAuMDIpCgojd3JpdGUuY3N2KENsdXN0ZXJNYXJrZXJzLCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QaGVub0lEL3NjUk5Bc2VxU29ydGVkL0dsaWEyUkdDbHVzdGVyTWFya2Vyc19uZXcuY3N2IikKCklkZW50cyhzZXUucSkgPC0gJ1JOQV9zbm5fcmVzLjAuMScKQ2x1c3Rlck1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc2V1LnEsIG9ubHkucG9zID0gVFJVRSkKCnRvcDUgPC0gQ2x1c3Rlck1hcmtlcnMgJT4lIGdyb3VwX2J5KGNsdXN0ZXIpICU+JSB0b3BfbihuPTUsIHd0ID0gYXZnX2xvZzJGQykKRG9IZWF0bWFwKHNldS5xLCBmZWF0dXJlcyA9IHRvcDUkZ2VuZSwgc2l6ZT0zLCBhbmdsZSA9OTAsIGdyb3VwLmJhci5oZWlnaHQgPSAwLjAyKQoKCmBgYApNYXJrZXJzIG9mIDIgYXJlIG1hdGNoaW5nIHdpdGggNSBwb3NzaWJseSBtZXJnZSB0aGVzZSB0b2dldGhlcgpDbHVzdGVyIDAgbWFya2VycyBkb24ndCBsb29rIHVwIHJlZ3VsYXRlZCBidXQgdGhlIGxpc3QgaXMgbG9uZwoKTG9vayBhdCB0aGUgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeShlbnJpY2hSKQoKc2V0RW5yaWNoclNpdGUoIkVucmljaHIiKSAjIEh1bWFuIGdlbmVzCiMgbGlzdCBvZiBhbGwgdGhlIGRhdGFiYXNlcwoKIyBsaWJhcmllcyB3aXRoIGNlbGwgdHlwZXMKCmRiIDwtIGMoJ0Rlc2NhcnRlc19DZWxsX1R5cGVzX2FuZF9UaXNzdWVfMjAyMScsCiAgICAgICAgJ0NlbGxNYXJrZXJfQXVnbWVudGVkXzIwMjEnLCdBemltdXRoX0NlbGxfVHlwZXNfMjAyMScpCgojIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IE5VTEwpCgojSSdsbCBydW4gdGhlIGNsdXN0ZXJzIG9uZSBhdCBhIHRpbWUKCk4xLmMwIDwtIENsdXN0ZXJNYXJrZXJzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAwICYgYXZnX2xvZzJGQyA+IDApCmdlbmVzIDwtIE4xLmMwJGdlbmUKCk4xLmMwLkVyIDwtIGVucmljaHIoZ2VuZXMsIGRhdGFiYXNlcyA9IGRiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1sxXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1syXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQpwbG90RW5yaWNoKE4xLmMwLkVyW1szXV0sIHNob3dUZXJtcyA9IDIwLCBudW1DaGFyID0gNDAsIHkgPSAiQ291bnQiLCBvcmRlckJ5ID0gIlAudmFsdWUiKQoKCk4xLkVyLmdlbmVzLjEgPC0gTjEuYzAuRXJbWzFdXSAlPiUgc2VsZWN0KFRlcm0sIEdlbmVzLCBDb21iaW5lZC5TY29yZSkKTjEuRXIuZ2VuZXMuMQoKTjEuRXIuZ2VuZXMuMiA8LSBOMS5jMC5FcltbMl1dICU+JSBzZWxlY3QoVGVybSwgR2VuZXMsIENvbWJpbmVkLlNjb3JlKQpOMS5Fci5nZW5lcy4yCgpOMS5Fci5nZW5lcy4zIDwtIE4xLmMwLkVyW1szXV0gJT4lIHNlbGVjdChUZXJtLCBHZW5lcywgQ29tYmluZWQuU2NvcmUpCk4xLkVyLmdlbmVzLjMKCgoKCgoKYGBgCgpUaGUgYWR1bHQgYnJhaW4gZG9lc24ndCBwcmVkaWN0IHJhZGlhbCBnbGlhIC0gdGhlcmUgYXJlIHJhZGlhbCBnbGlhIGJ1dCBJIGJlbGlldmUgdGhlc2UgYXJlIGRpZmZlcmVudCBmcm9tIHRoZSAnbmV1cm9ibGFzdCcgdHlwZSByYWRpYWwgZ2xpYQoKSSB3aWxsIHVzZSBhIGRldmVsb3BpbmcgYnJhaW4gcmVmZXJlbmNlCgpgYGB7cn0KRGltUGxvdChzZXUucSkKCmBgYAoKCgpgYGB7cn0KCnNldS5xIDwtIHJlYWRSRFMocGFzdGUocGF0aHdheSwiR2xpYTJMYWJsZWRTZXUwMzEwMjAyMi5SRFMiLHNlcCA9ICIiKSkKCiMgbWlkYnJhaW4gYW5kIHN0cmlhdHVtCgpzZXUuciA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QdWJsaWNEYXRhL0JoYWR1cmlfd2hvbGVCcmFpbi9CaGFkdXJpX21pZGJyYWluX3N0cmlhdHVtLlJEUyIpCgpJZGVudHMoc2V1LnIpIDwtICJjZWxsX2NsdXN0ZXIiCgojIGZpbmQgdGhlIHJlZmVyZW5jZSBhbmNob3JzCmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXUuciwgcXVlcnkgPSBzZXUucSwgZGltcyA9IDE6MjUpCnByaW50KCJnZXR0aW5nIHByZWRpY3Rpb25zIikKcHJlZGljdGlvbnMgPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IGFuY2hvcnMsIHJlZmRhdGEgPSBzZXUuciRjZWxsX2NsdXN0ZXIpCnNldS5xIDwtIEFkZE1ldGFEYXRhKHNldS5xLCBtZXRhZGF0YSA9IHByZWRpY3Rpb25zKQpwcmludCh0YWJsZShzZXUucSRwcmVkaWN0ZWQuaWQpKQoKSWRlbnRzKHNldS5xKSA8LSAncHJlZGljdGVkLmlkJwpzZXUucSRCaGEubWlkLnN0cmkucHJlZCA8LSBJZGVudHMoc2V1LnEpCnByaW50KHRhYmxlKHNldS5xJEJoYS5taWQuc3RyaS5wcmVkKSkKCgpzZXUucSRwcmVkaWN0ZWQuaWQgPC0gaWZlbHNlKHNldS5xJHByZWRpY3Rpb24uc2NvcmUubWF4ID4gMC45NSwgc2V1LnEkcHJlZGljdGVkLmlkLCAibm9uZSIpCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQmhhLm1pZC5wcmVkLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5taWQucHJlZC50aHJlc2gnLCBsYWJlbCA9IFRSVUUpCnRhYmxlKHNldS5xJEJoYS5taWQucHJlZC50aHJlc2gpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5taWQucHJlZC50aHJlc2gnKQoKCiMgcmVhZCBpbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQKIyB3aG9sZSBicmFpbiBCaGFkdXJpIGRvd24gc2FtcGxlZApzZXUuciA8LSByZWFkUkRTKCIvVXNlcnMvcmhhbGVuYXRob21hcy9Eb2N1bWVudHMvRGF0YS9zY1JOQXNlcS9QdWJsaWNEYXRhL0JoYWR1cmlfd2hvbGVCcmFpbi9CaGFkdXJpX2Rvd25zYW1wbGUuUkRTIikKCklkZW50cyhzZXUucikgPC0gImNlbGxfY2x1c3RlciIKCiMgZmluZCB0aGUgcmVmZXJlbmNlIGFuY2hvcnMKYW5jaG9ycyA8LSBGaW5kVHJhbnNmZXJBbmNob3JzKHJlZmVyZW5jZSA9IHNldS5yLCBxdWVyeSA9IHNldS5xLCBkaW1zID0gMToyNSkKcHJpbnQoImdldHRpbmcgcHJlZGljdGlvbnMiKQpwcmVkaWN0aW9ucyA8LSBUcmFuc2ZlckRhdGEoYW5jaG9yc2V0ID0gYW5jaG9ycywgcmVmZGF0YSA9IHNldS5yJGNlbGxfY2x1c3RlcikKc2V1LnEgPC0gQWRkTWV0YURhdGEoc2V1LnEsIG1ldGFkYXRhID0gcHJlZGljdGlvbnMpCnByaW50KHRhYmxlKHNldS5xJHByZWRpY3RlZC5pZCkpCgpJZGVudHMoc2V1LnEpIDwtICdwcmVkaWN0ZWQuaWQnCnNldS5xJEJoYS5wcmVkIDwtIElkZW50cyhzZXUucSkKcHJpbnQodGFibGUoc2V1LnEkQmhhLnByZWQpKQpEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdCaGEucHJlZCcpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycpCgpzZXUucSRwcmVkaWN0ZWQuaWQgPC0gaWZlbHNlKHNldS5xJHByZWRpY3Rpb24uc2NvcmUubWF4ID4gMC45NSwgc2V1LnEkcHJlZGljdGVkLmlkLCAibm9uZSIpCklkZW50cyhzZXUucSkgPC0gJ3ByZWRpY3RlZC5pZCcKc2V1LnEkQmhhLm1pZC5wcmVkLnRocmVzaCA8LSBJZGVudHMoc2V1LnEpCkRpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ0JoYS5wcmVkLnRocmVzaCcsIGxhYmVsID0gVFJVRSkKdGFibGUoc2V1LnEkQmhhLm1pZC5wcmVkLnRocmVzaCkKRGltUGxvdChzZXUucSwgZ3JvdXAuYnkgPSAnQmhhLnByZWQudGhyZXNoJykKCgpgYGAKCgoKCgpBZGQgc29tZSBjZWxsIHR5cGUgYW5ub3RhdGlvbnMKCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC4yJwoKY2x1c3Rlci5pZHMgPC0gYygiR2xpYTEiLCJSRzEiLCJHbGlhMiIsIlJHMiIsIk5ldXJvbnNJbW1hdHVyZSIsIlJHMyIpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRzdWJncm91cHMgPC0gSWRlbnRzKHNldS5xKQoKI0RpbVBsb3Qoc2V1LnEsIGdyb3VwLmJ5ID0gJ1JOQV9zbm5fcmVzLjAuMicsIGxhYmVsID0gVFJVRSkKRGltUGxvdChzZXUucSwgcmVkdWN0aW9uID0gInVtYXAiLCBsYWJlbCA9IFRSVUUsIGdyb3VwLmJ5ID0gJ3N1Ymdyb3VwcycsIHJlcGVsID0gVFJVRSkKCgoKYGBgCgpgYGB7cn0KIyBzYXZlIGZpbGUKc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMkxhYmxlZFNldTAzMTAyMDIyLlJEUyIpCgpgYGAKCk1haW4gY2VsbCBncm91cHMKCmBgYHtyfQoKSWRlbnRzKHNldS5xKSA8LSAnUk5BX3Nubl9yZXMuMC4yJwoKY2x1c3Rlci5pZHMgPC0gYygiUmFkaWFsIEdsaWEiLCJSYWRpYWwgR2xpYSIsIlJhZGlhbCBHbGlhIiwiUmFkaWFsIEdsaWEiLCJOUEMiLCJPdGhlciIpCgpuYW1lcyhjbHVzdGVyLmlkcykgPC0gbGV2ZWxzKHNldS5xKQpzZXUucSA8LSBSZW5hbWVJZGVudHMoc2V1LnEsIGNsdXN0ZXIuaWRzKQpzZXUucSRDZWxsX1R5cGVzIDwtIElkZW50cyhzZXUucSkKCiNEaW1QbG90KHNldS5xLCBncm91cC5ieSA9ICdSTkFfc25uX3Jlcy4wLjInLCBsYWJlbCA9IFRSVUUpCkRpbVBsb3Qoc2V1LnEsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgbGFiZWwgPSBUUlVFLCBncm91cC5ieSA9ICdDZWxsX1R5cGVzJywgcmVwZWwgPSBUUlVFKQoKc2F2ZVJEUyhzZXUucSwgIi9Vc2Vycy9yaGFsZW5hdGhvbWFzL0RvY3VtZW50cy9EYXRhL3NjUk5Bc2VxL1BoZW5vSUQvc2NSTkFzZXFTb3J0ZWQvb2Jqcy9HbGlhMkxhYmxlZFNldTAzMTAyMDIyLlJEUyIpCgpgYGAKClByb3BvcnRpb25zIG9mIGNlbGwgdHlwZXMKCmBgYHtyfQoKdGFibGUoc2V1LnEkQ2VsbF9UeXBlcykKZGltKHNldS5xKQoKcHJwIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoc2V1LnEkQ2VsbF9UeXBlcykpCnBycAoKcHJwJHByb3AgPC0gcHJwJEZyZXEvc3VtKHBycCRGcmVxKSoxMDAKcHJwJFNhbXBsZSA8LSAnUmFkaWFsR2xpYScKcHJwCgoKYGBgCgpJJ2xsIGNhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbnMgZm9yIGVhY2ggY2VsbCB0eXBlIGFuZCBtYWtlIGEgdGFibGUgb3IgcGxvdCBpbiB0aGUgY29tcGFyaXNvbiB3b3JrYm9vay4gCgoKCg==